Promise 中异步执行的代码在哪里?

Posted

技术标签:

【中文标题】Promise 中异步执行的代码在哪里?【英文标题】:Where is code executed asynchronously in a Promise? 【发布时间】:2017-09-20 01:48:37 【问题描述】:

以下javascript函数来自You Don't Know JS: Async & Performance。据我了解,第一条评论// start doing something that could take a while 具有误导性。实际可能异步完成的代码部分位于传递给Promise 构造函数的函数中。

function foo(x) 
    // start doing something that could take a while *misleading comment*

    // construct and return a promise
    return new Promise( /* executor */ function(resolve,reject)
        // eventually, call `resolve(..)` or `reject(..)`,
        // which are the resolution callbacks for
        // the promise.
     );

我会通过以下方式修复它:

function foo(x) 
    // construct and return a promise
    return new Promise( /* executor */ function(resolve,reject)
        // start doing something that could take a while
        // then foo returns the newly created Promise
        // eventually, call `resolve(..)` or `reject(..)`,
        // which are the resolution callbacks for
        // the promise.
     );

【问题讨论】:

是的,你的构造可能更好,尽管代码也可以在你创建 Promise 之前执行。我不确定你在这里问什么你还不知道答案的问题。 代码太抽象,无法真正找出错误,或者“宣称”一个比另一个更好 答案是:无处可去。 promise 执行器函数同步运行。您可以从那里启动一个异步操作,最终调用resolvereject(可能使用回调)。认为 promise executor 函数本身以某种方式异步运行是一个常见的误解——这不是真的——我担心你的问题(以及确认的答案,虽然是正确的)可能会让人们误入歧途。您可以对其进行编辑以更清楚地说明这一点吗? @jfriend00 代码可以在你创建promise之前执行,但是你必须引入一个辅助变量来调用resolve或reject,see this comment 【参考方案1】:

答案是:promise 中没有任何地方可以异步执行代码。 *

虽然您展示的第二种形式稍有优势,但这仅仅是因为它更一致地处理错误(期望调用者同时处理异常和拒绝是一种糟糕的形式)。

解决您的主要困惑:promise executor 函数同步运行。来自MDN:

executor 函数由 Promise 实现立即执行,传递 resolve 和 reject 函数(在 Promise 构造函数甚至返回创建的对象之前调用 executor)。

还有spec:

从执行器函数返回并不意味着延迟操作已经完成,而只是表示最终执行延迟操作的请求已被接受。

由您决定是否启动最终调用resolvereject 的任何异步操作:

new Promise(resolve => setTimeout(resolve, 2000))
  .then(() => console.log("Async"));

console.log("Sync");

最重要的是,Promise 构造函数仅用于包装不支持 Promise 的遗留函数。 Don't use it for anything else.


*) 实际上,.then 保证传递给它的函数永远不会立即运行,所以我的第一句话并不完全正确。它们最快可以运行在事件循环的同一运行结束时,在微任务队列中。几乎不是你想要的。

【讨论】:

“它们可以运行的最快时间是在事件循环的同一运行结束时,在微任务队列中。”微任务队列是否与作业队列相同(如here所述)? 是的。请注意,您附加到 .then 的任何函数仍然作为单线程阻塞代码运行,就像所有其他 JavaScript 一样。它只是稍后运行。真正在另一个线程上运行 JS 的唯一方法是 launch a worker。【参考方案2】:

是的,这应该解决(感谢提交this issue)。

这让我想起了difference between the deferred pattern and the revealing constructor pattern。在 promise 构造函数回调中启动异步任务有两个好处:

如果它同步抛出(例如语法错误、方法调用中的拼写错误等),异常将被隐式捕获并拒绝承诺 resolvereject 回调已经在范围内,可以作为回调传递给异步进程。

【讨论】:

以上是关于Promise 中异步执行的代码在哪里?的主要内容,如果未能解决你的问题,请参考以下文章

[万字详解]JavaScript 中的异步模式及 Promise 使用

对Promise中的resolve,reject,catch的理解

Promise的那些事儿

ES6新特性:Javascript中内置的延迟对象Promise

关于Promise的总结

promise