Javascript 为什么我的异步函数要在实现之前执行?

Javascript 为什么我的异步函数要在实现之前执行?,javascript,Javascript,我编写了一个小程序,比较了.then()方法和async/await方法的承诺履行情况。代码运行正常,但输出是按意外顺序接收的。有人能解释一下为什么输出是按当前顺序的吗 const backend=(num)=>{ const someVar=新承诺((解决、拒绝)=>{ 如果(数值%2==0){ 解析(`数字${num}是偶数。`); }否则{ 拒绝(`数字${num}是奇数。`); } }) 返回someVar; } 常量内置函数=(num)=>{ 后端(num) .then(messag

我编写了一个小程序,比较了
.then()
方法和
async/await
方法的承诺履行情况。代码运行正常,但输出是按意外顺序接收的。有人能解释一下为什么输出是按当前顺序的吗

const backend=(num)=>{
const someVar=新承诺((解决、拒绝)=>{
如果(数值%2==0){
解析(`数字${num}是偶数。`);
}否则{
拒绝(`数字${num}是奇数。`);
}
})
返回someVar;
}
常量内置函数=(num)=>{
后端(num)
.then(message=>console.log(message))
.catch(message=>console.log(message));
}
常量AsyncWait=async(num)=>{
试一试{
const response=等待后端(num);
控制台日志(响应);
}捕获(错误){
console.log(错误);
}
}
内置式(2);
内置式(3);
异步等待(4);

异步等待(5)对于微任务解析,每个方法调用都单独排队。所以执行的顺序是:

  • 第一次呼叫排队
  • 第二次呼叫排队
  • 第三次呼叫排队
  • 第四次呼叫排队
  • 首先
    。然后
    激发,console.logged
  • 第二个
    。然后
    激发、拒绝、
    。捕获
    处理程序排队(未调用)
  • 异步/等待调用,console.logged
  • 第二次调用async/await、rejection、catch块排队
  • .catch
    解析,console.logged
  • catch块解析为最终日志
在关于切换捕获顺序的评论中,向巴尔马提供道具

为了简单地说明(明显地),考虑一个计数器和一个允诺函数,它递增并递减它:

let i = 0;
const log = Promise.resolve()
  .then(() => console.log(++i))
  .then(() => console.log(--i));

log();
log();

这将打印1 2 1 0而不是1 0 1 0。如果你仔细想想,这是有一定意义的:方法链在任何一步都可能失败,因此运行时会将第一个调用和第二个
排队。然后
只在第一个调用完成后才会排队。否则,如果第一个调用失败(被拒绝),它将不得不返回并从回调队列中删除推测性排队的第二个调用。

在得到答案之前,值得一提的是,依赖异步调用的执行顺序不是一个好的做法。有两种方法可以实现您的预期行为。首选方法应为:

(async() => {
   await builtInFuncs(2);
   await builtInFuncs(3);
   await asyncAwait(4);
   await asyncAwait(5);
})();
或者,您可以依赖ECMAScript标准保证的PromiseReactionJobs的执行顺序。您需要将内置函数重新定义为:

const builtInFuncs = (num) => {
   backend(num).then(
      message => console.log(message),
      message => console.log(message)
   );
}
请注意,onCompleted和onRejected处理程序都传递给.then()函数

观察到的执行顺序的实际原因相当复杂,但发生的情况如下:

  • 内置函数(2)
    被调用

  • 内置函数(2)
    调用
    后端(2)。然后()

  • backend(2).then()
    实际上是排队
    console.log(2)

  • 后端(2)。然后()

  • backend(2).then().catch()
    注意到
    promise1
    对象上的
    onRejected
    处理程序

  • 内置函数(3)
    被调用

  • 内置函数(3)
    调用
    后端(3)。然后()

  • 后端(3)。然后()
    将一个虚拟
    onRejected
    处理程序排入队列,因为未指定任何处理程序

  • 后端(3)。然后()

  • 后端(3).then().catch()
    注意到调用
    console.log(3)
    on
    promise2

  • asyncwait(4)
    被调用

  • asyncwait(4)
    有效地调用
    后端(4)。然后()

  • 后端(4)。然后()
    将继续执行try分支的oncompleted处理程序排入队列

  • asyncwait(5)
    被调用

  • asyncwait(5)
    有效地调用
    后端(5)。然后()

  • 后端(5)。然后()
    将继续捕获分支的onRejected处理程序排入队列

  • 打印
    console.log(2)
    的处理程序将退出队列

  • dummy
    onRejected
    处理程序退出队列

  • promise2
    enqueues
    onRejected
    handler它注意到哪个处理程序打印了
    console.log(3)

  • 继续try分支的onCompleted处理程序已退出队列

  • 继续捕获分支的onRejected处理程序已退出队列

  • 打印
    console.log(3)
    的处理程序将退出队列


  • 请注意,
    后端
    返回的
    承诺将立即得到解决或拒绝。如果没有,则会涉及更多步骤,但实际上会观察到相同的行为。

    我怀疑解决方案在事件队列中的优先级高于拒绝。您可以通过将顺序切换为:
    .catch(…)。然后(…)
    但无法以类似方式重新排序
    try/catch
    。@Barmar我根据您的建议更新了一个示例。@JaredSmith我认为try-and-catch的catch块不会排队。当const response=wait backend(num)时;catch块将运行,因为它不是对异步函数的调用,而是等待返回(已解析或已拒绝)的同步代码块。这就是为什么输出是2,4,5,3,而不是2,4,3,5。@MostafaAmin不,async/await不阻塞,它只是糖加于人的承诺。您可以通过同时运行多个异步调用来测试这一点。