【发布时间】:2019-10-30 06:17:49
【问题描述】:
console.log('1')
setTimeout(() => {
console.log('2')
}, 0)
function three() {
return new Promise(resolve => {
setTimeout(() => {
return new Promise(resolve => resolve('3'))
},0)
})
}
three().then(result => console.log(result))
console.log('4')
这段代码sn-p输出1 4 2
根据我对 javascript 的事件循环和并发模型的理解,这是我所期望的行为。但这给我留下了一些挥之不去的问题。
在回答这些问题之前,我先分解一下我对这段代码 sn-p 的理解。
为什么代码输出 1
不需要解释
为什么代码输出 4
输出 2 的回调在 0 毫秒后被加载到事件队列(又名宏任务队列)中,但直到主调用堆栈被清空后才会执行。
即使three 是一个立即解决的承诺,它的代码也会被加载到作业队列(又名微任务队列)中,并且在主调用堆栈被清空之前不会被执行(不管事件的内容如何)队列)
为什么代码输出 2
在console.log(4) 之后,主调用堆栈为空,javascript 寻找下一个回调以加载到主堆栈上。可以肯定的是,此时某个“工作线程”已经将输出 2 的回调函数放入宏任务队列中。这被加载到堆栈上并输出 2。
为什么代码不输出 3
这对我来说有点模糊。函数three 在主线程中返回一个then-ed 的promise。通过then 传递的回调函数被加载到微任务队列中,并在宏任务队列中的下一个任务之前执行。因此,虽然您可能认为它会在记录 2 的回调之前运行,但实际上它在理论上根本不可能运行。这是因为 Promise 仅通过其 setTimeout 的回调函数解析,并且该回调函数(由于 setTimeout)仅在主执行线程(等待 promise 解析的同一线程)为空时才会运行。
为什么这让我烦恼
我正在尝试构建一个完整的关于 javascript 如何处理并发的理论心理模型。该模型中缺少的部分之一是网络请求、承诺和事件循环之间的关系。以上面的代码 sn-p,假设我用某种网络请求替换了three 的 setTimeout(在异步 Web 开发中很常见)。假设网络请求的行为类似于 setTimeout,因为当“工作线程”完成时,回调被推送到宏任务队列,我很难理解回调是如何执行的。但这是字面上一直在发生的事情。
谁能帮我理解?我目前对 js 并发性的理解有什么遗漏吗?我做了不正确的假设吗?这真的有任何意义吗?哈哈
【问题讨论】:
-
您正在覆盖您的
resolve回调,该回调永远不会被调用,因此您的承诺始终处于未决状态。 -
将
return new Promise(resolve => resolve('3'))更改为return new Promise(resolve2 => resolve('3'))。但是,在 settimeout 的回调中没有任何承诺。
标签: javascript node.js multithreading promise