一道神奇的async quiz

787 阅读1分钟

原文链接:https://github.com/AlvinYuXT/blog/issues/4

Jake Archibald最近在twitter上出了一道简单的quiz题目,问大家x的输出是什么。代码如下:

let x = 0;

async function test() {
    x+= await 2;
    console.log(x);
}

test();
x += 1;
console.log(x)

看到这里我想,这不就是在考microTask和marcoTask吗?那么答案应该不难,执行顺序是这样的:

  1. 先执行test函数,test函数中直接就进入到x+=await 2了,那么放到microTask中
  2. 执行x+=1
  3. 执行console.log(x),输出结果是1
  4. 当前作用域没有macro要执行了,所以执行microTask,执行x+=await 2;console.log(x)输出结果应该是3

错!!!

实际上不是这样的,而这里我对async/await中什么内容放到microtask也存在错误的理解。正确的执行顺序是这样的

  1. 执行test函数此时x+=await 2等价于x = x + await Promise.resolve(2),表达式右边在求值的时候遇到了await,所以这个时候x已经求值了,也就是说上面的函数等价于x = 0 + await Promise.resolve(2)。这里还有一点需要注意的是Promise只有then和catch里面的是真正异步执行的,想new Promise(resolve=>{console.log(123)})这种事直接同步执行的,而这里的Promise.resolve(2)也是同步执行的。
  2. 这个时候继续执行x+=1;console.log(x)输出的结果是1
  3. macroTask执行结束之后去执行microTask,这个时候就会回来执行x = 0 + 2;console.log(x),所以这个时候输出的是2

注意:await后面的语句并不是放到micro去执行的,而是直接同步执行的。也就是说await并不直接将后面要执行的代码放到microTask中,await只是等待microTask执行完才把await后面的表达式求值结果返回给左值。