对从异步函数返回的承诺感到非常困惑
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 => 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 => const ret = Promise.resolve(123); resolve(ret); )
。 @imheretolearn1 微任务是resolve(ret)
调用中的ret.then(…)
调用(访问内部promise 的结果以用它解决外部promise)。理论上没有必要,但 ECMAScript 规范要求这样做。以上是关于对从异步函数返回的承诺感到非常困惑的主要内容,如果未能解决你的问题,请参考以下文章