【问题标题】:How Job queue works with promise?作业队列如何与承诺一起工作?
【发布时间】:2021-02-28 01:18:37
【问题描述】:

我正在学习 JS 中的 Promise,并对 Promise 如何在幕后与 Job 队列一起工作感到好奇。为了解释我的困惑,我想向您展示这段代码:

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000);

}).then(function(result) {

  alert(result); // 1

  return new Promise((resolve, reject) => { // (*)
    setTimeout(() => resolve(result * 2), 1000);
  });

})

如果你看上面的代码,then()的回调是不是预先放入Job队列,等待promise解决?还是只有在promise解决后才将then()的回调推入作业队列?

【问题讨论】:

  • 注意:不要将alert 用于此类事情。 alert 的行为是非标准的,并且因浏览器而异(甚至使用相同的浏览器,取决于选项卡是否处于活动状态)。

标签: javascript promise


【解决方案1】:

当需要调用 promise 回调时,作业根本不会进入标准作业队列 (ScriptJobs);它进入 PromiseJobs 队列。处理 PromiseJobs 队列,直到 ScriptJobs 队列中的每个作业结束时它为空。 (规范中的更多内容:Jobs and Job Queues。)

我不确定您对代码的期望是什么输出,但让我们举一个更简单的例子:

console.log("top");
new Promise(resolve => {
    setTimeout(() => {
        console.log("timer callback");
    }, 0);
    resolve();
})
.then(() => {
    console.log("then callback 1");
})
.then(() => {
    console.log("then callback 2");
});
console.log("bottom");

可靠的输出是:

最佳 底部 然后回调 1 然后回调 2 定时器回调

因为:

  1. 运行该脚本的 ScriptJobs 作业运行
  2. console.log("top") 运行
  3. promise 执行器函数代码运行,它
    • 为“立即”安排一个计时器作业,该作业将立即或几乎立即进入 ScriptJobs 队列
    • 通过不带参数调用resolve 来履行承诺(这意味着在调用then 之前解决承诺)(实际上就像用undefined 调用它一样,它不是thenable 触发承诺的履行)。
  4. 第一个 then 连接第一个履行处理程序,将 PromiseJobs 作业排队,因为承诺已经履行
  5. 第二个 then 连接第二个履行处理程序(不排队作业,等待第一个 then 的承诺)
  6. console.log("bottom") 运行
  7. 当前 ScriptJob 作业结束
  8. 引擎处理正在等待的 PromiseJobs 作业(第一个履行处理程序)
  9. 输出 "then callback 1" 并履行第一个 then 的承诺(通过返回)
  10. 这会在 PromiseJobs 队列中排队另一个作业,以便回调到第二个履行处理程序
  11. 由于 PromiseJobs 队列不为空,下一个 PromiseJob 被拾取并运行
  12. 第二个履行处理程序输出"then callback 2"
  13. PromsieJobs 为空,因此引擎会选择下一个 ScriptJob
  14. ScriptJob 处理定时器回调并输出"timer callback"

在 HTML 规范中,它们使用略有不同的术语:“task”(或“macrotask”)用于 ScriptJobs 作业,“microtask”用于 PromiseJobs 作业(和其他类似作业)。

关键点是:在 ScriptJob 完成时处理所有在 ScriptJob 期间排队的 PromiseJobs,其中包括任何 PromiseJobs它们 队列;只有当 PromiseJobs 为空时,才会运行下一个 ScriptJob。

【讨论】:

  • 嗨,好的,我知道当 promise 解决时 thenable 回调被发送到作业队列。但问题是,为什么在回调从 Promise 中获取必要的数据之后发送 thenable 回调?希望你明白我的意思:)
  • @wewq - 对不起,我不明白这个问题。
  • @k1an - 当编辑被拒绝时,请不要再次进行相同的编辑。在实现时,没有附加到承诺的承诺反应,这意味着没有承诺反应作业要排队。但感谢您尝试保持准确! :-)
  • then 之后的部分是添加到微队列的回调,并且由于此队列的优先级高于宏队列(保存 setTimeout),因此 setTimeout 的 console.log 最后发生?
  • @variable - 没错,是的。
【解决方案2】:

我会说 then() 的回调仅在 承诺得到解决后才被推入作业队列。

如果您将第一个超时时间更改为 3000,则运行代码并等待 3 的提醒 1。这是因为您必须等待承诺在 3 秒内得到解决。

您可以从这里得到答案:https://stackoverflow.com/a/30910084/12733140

promiseA.then() 的回调是一个任务

  • promiseA 已解决/拒绝:任务将在本轮事件循环中被推入微任务队列。
  • promiseA 待处理:任务将在下一轮事件循环中被推入微任务队列(可能是下一轮)

所以这里的microtask和你上面提到的“job”一样,只是promise被resolved或者rejected,回调会被推送到job/microtask队列中。

【讨论】:

  • 嗨,好的 thenable 回调在 promise 解决后执行。但为什么?既然我们在 promise 被解决后得到数据,那么为什么需要将 thenable 回调发送到作业队列,并从 promise 接收到数据。希望你明白我的意思
  • 对不起,我实际上不确定你想问什么。但是 JavaScript 是单线程语言,它只有一个线程作为主线程来执行同步代码,异步回调必须先被推送到任务队列中,直到主线程为空才会被调用。您可以尝试将 0 传递给 settimeout 函数,并且在主线程中完成同步内容后仍会调用回调。这基本上就是事件循环的工作方式。 YouTube 上也有一段介绍事件循环的经典视频,有兴趣可以搜索一下。希望这是您要问的问题。
猜你喜欢
  • 1970-01-01
  • 2018-07-15
  • 2014-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-11
  • 1970-01-01
  • 2014-01-22
相关资源
最近更新 更多