【发布时间】:2022-01-07 10:42:35
【问题描述】:
一年多来,我一直在尝试完全理解 async 和 Promise 的概念,但我仍然无法掌握它的全部内容。
大多数 Promises 文档都是关于“非阻塞”的,示例代码通过这种方式模拟带有 setTimeout 的长请求:
console.log('foo1');
let promiseA = new Promise(resolv => {
setTimeout(() => resolv('finished'), 1000);
})
promiseA.then(answer => {
console.log(answer);
});
console.log('foo2');
产生于
"foo1"
"foo2"
"finished"
但这不是同时混合了两个不同的概念吗?据我了解,setTimeout 函数是浏览器/运行时函数,setTimeout 精确触发 浏览器线程 中的内部计数器,您的回调函数在 重新添加到 JavaScript 引擎通过macrotask队列超时结束,从而让你下面调用的js函数依然执行。
但是没有使用 setTimeout 的例子呢?考虑一下:
let promiseA = new Promise(resolv => {
uselesscounter = 0;
for(let i = 0; i < 1000000000; i++) {
uselesscounter+= 1;
}
resolv('finished');
})
promiseA.then(answer => {
console.log(answer);
})
console.log('foo2')
从上面的例子中,我们可能认为我们将有“foo1”、“foo2”,然后循环将花费一些时间,我们将在几秒钟后“完成”。这不是正在发生的事情。由于执行程序代码是直接执行的(而不是使用任何浏览器 api)它阻塞了下面的“foo2”,尽管最后解析回调被添加到微任务队列并在“foo2”之后显示.
那么,像这样不使用浏览器 api 函数的回调有意义吗?
【问题讨论】:
-
不,promise executor 不会在“其他地方”运行。没有多线程或其他并行执行神奇地发生 - 如果你在执行器中阻塞线程,你就会阻塞线程。
-
假设您使用“浏览器 api 函数”作为任何类型的异步主机函数的同义词,我想唯一的原因是如果您需要满足某些 API 要求,或者如果您构建一个您自己的 API期望最终是异步的。但在这些情况下,您也可以直接使用
Promise.resolve(someValue)。在主线程上设置阻塞进程是没有意义的。 -
Promise 仅作为一种处理异步操作的方式真正“有用”,其中大部分工作可以委托给其他线程。工作线程、系统线程(通过运行时(浏览器/节点/等))或整个其他机器(例如通过 HTTP 请求)。如果您在 Promise 中所做的工作完全发生在 JS 中(而不是在 worker 中),那么该代码将在某个时候阻塞主线程。无论它是否在 promise 块内执行。
-
@VLAZ 超时计数器确实必须在 console.log('foo2');运行正确吗?我认为它是在浏览器中运行的,而不是在 javascript 中,就像一小块 C(或特定的浏览器语言)在内部进行倒计时,当它完成时,告诉 javascript“我完成了,你可以执行你的函数” (将其添加到宏任务队列中)
-
@YoannM whether or not an async task blocks the thread depends on the task。
setTimeout永远不会阻塞。其他示例可能是 HTTP 调用或工作线程 - 如果操作在主线程之外完成,则它是非阻塞的。但是,如果您有一个循环,则不会因为将其放入 Promise 执行程序而从阻塞代码转换为非阻塞代码。
标签: javascript asynchronous promise callback