对从异步函数返回的承诺感到非常困惑

Posted

技术标签:

【中文标题】对从异步函数返回的承诺感到非常困惑【英文标题】:Really confused about promise being returned from async function 【发布时间】:2021-08-22 13:27:30 【问题描述】:

所以我知道异步函数在遇到 await 关键字时会立即返回一个待处理的 Promise,如果没有使用 await 关键字,异步函数返回的 Promise 将被解析为我们从异步函数返回的值,例如这个:

async function f() 
    let p = await (new Promise(r => 
        setTimeout(r, 2000, 100)
    ))
    return p;


let p = f();
console.log(p); // pending, because its awaiting.

第二个例子:

async function f() 
    let p = 'someValue';
    return p;

let p = f();
console.log(p); // <fulfilled>: "someValue"

第二个例子也很有意义,我们返回 'someValue',正如 MDN 所说,它包含在 Promise.resolve('someValue') 中,这就是返回的内容。

现在,这段代码打印的内容让我非常困惑,它记录了一个未决的承诺!

(我知道我没有在这里使用 await,这是故意的)

async function f() 
    let p = new Promise(r => r(113));
    return p;

let af = f();
console.log(af)

我们正在我们的 async f 函数中创建一个立即解决的承诺,这就是我在我默认创建的异步函数承诺中返回的承诺,那么为什么该日志处于挂起状态?

即使它处于未决状态,当我展开该对象时,我也会同时看到:

[[PromiseResult]]: 113

有人可以帮忙吗?

【问题讨论】:

r =&gt; r(value) 在下一个滴答声中解析,因此您关于创建已解析承诺的陈述不正确。见Promise.resolve。请注意,在 devtools 中扩展之后会发生很多滴答声,所以当然会有结果。 @tehhowch - 这对我来说感觉很新鲜,因为非正式地,只要调用 executor 函数的第一个参数,我就知道,promise 就解决了。在我们的例子中,这是异步发生的,所以当我们点击“return p;”时在我们的最后一个例子中,它应该返回一个已解决的承诺,不是吗?我们没有任何.then,我们将其回调函数放入微任务队列中以供稍后执行。 @tehhowch 仅当 value 可用时。有了原始值,它应该立即实现。真正需要额外滴答的是resolve(p)async function 返回的承诺。 来自docs:'异步函数总是返回一个promise。如果异步函数的返回值不是明确的承诺,它将被隐式包装在承诺中。' 我还是不明白为什么“r => r(value)”会在下一个tick时被调用?执行程序函数不应该同步运行吗? 【参考方案1】:

这可能是因为 javascript 引擎需要“扁平化”返回的承诺。

console.log(Promise.resolve(113)); //=> fulfilled
const a = async () => 113;
console.log(a()); //=> fulfilled
const b = async () => Promise.resolve(113);
console.log(b()); //=> pending

最后一个场景记录了一个未决的承诺。这可能是因为 JavaScript 引擎看到您从异步函数返回了一个 Promise。在这种情况下,当您 await b() 时,您期望 113 作为结果。异步函数总是返回一个 Promise。如果这个 Promise 的解析值(异步函数的返回值)是另一个 Promise,那么这个 Promise 就是“扁平化”的。

为了更好地说明这一点,将b 的返回值想象为(Firefox 控制台表示法):

Promise  <state>: "pending", <value>: Promse  <state>: "fulfilled", <value>: 123  

而第一个代码块和a 的返回值都是:

Promse  <state>: "fulfilled", <value>: 123 

嵌套 promise 的扁平化由引擎完成,并且可能添加到事件循环的末尾,因此在当前运行的上下文之后运行。这意味着外部承诺在发生之前待处理

这种行为可能取决于引擎,所以我不会依赖这种行为。 Promise 实例是对最终价值的承诺,应该这样对待。永远不要期望承诺会立即解决。如果您期望一个已履行的承诺,您最好使用正常的同步函数,这样可以避免使用承诺。

【讨论】:

这有点意思,我很好奇在所有同步代码完成后,将把什么函数放到微任务队列中执行。 b() 等价于new Promise(resolve =&gt; const ret = Promise.resolve(123); resolve(ret); )。 @imheretolearn1 微任务是resolve(ret) 调用中的ret.then(…) 调用(访问内部promise 的结果以用它解决外部promise)。理论上没有必要,但 ECMAScript 规范要求这样做。

以上是关于对从异步函数返回的承诺感到非常困惑的主要内容,如果未能解决你的问题,请参考以下文章

如何对从包含异步的函数返回的值使用 List.sort?

从异步函数返回的承诺中获取价值

对功能子类型感到困惑

返回承诺的函数必须是异步的

异步函数返回承诺,而不是值

异步函数返回承诺,而不是值