一、开始
很多的面试都会考查面试者对 js 异步模型的熟悉程度(本文仅代表 Node11 以后和浏览器的状况),会出一些简单的 setTimeout 和 Promise 结合的题目,例如:
1 2 3 4 5 6
   | console.log(1) setTimeout(function() { console.log(2) }) new Promise(function(resolve) {     console.log(3)     resolve(4) }).then(function(val) { console.log(val) })
   | 
 
然后问你顺序并问你为什么?对异步模型有了解的人应该都能说出来个大概(1,3,4,2)。
二、配合 async
如果出的题是下面这一道,你能说出他的输出顺序吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | var a = async function() {   console.log(1)   await run()   console.log(3) } a()
  setTimeout(function() {   console.log(4) })
  var run = () => { console.log(2) }
  var b = new Promise((resolve) => {   console.log(5)   resolve(6)   console.log(7) }).then((val) => console.log(val))
  console.log(8)
  | 
 
如果你不了解 async 函数的运行机制,你可能一时语塞。
如果我把 run 函数变一下,你仍然能说出正确的顺序吗?
1
   | var run = () => Promise.resolve(2).then(val => console.log(val))
   | 
 
三、揭秘
async 函数可以看成是基于Generator的一个语法糖,关于 generator 函数本文不作赘述,网上有很多写得很棒的文章,有兴趣可以了解一下,这里主要讲一下 async 函数包装后的代码运行顺序。
generator 函数调用之后会给我们一个迭代器,提供给我门一个 next(返回给我们 value 和 done)的方法供我们惰性执行函数。对 async 函数来说,await 后面的值会被封装成一个 Promise,async 函数帮我们做的就是执行这一个迭代器到结束。下面我们可以根据这个思路仿写一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | function co(genF) {   return new Promise(function(resolve, reject) {     const gen = genF(); // 先执行一次,拿到迭代器对象
      function exec(nextF) {       let next;       try {         next = nextF(); // 执行next方法       } catch(e) {         return reject(e);       }       if(next.done) { // 如果done===true,则代表迭代结束         return resolve(next.value);       }       Promise.resolve(next.value).then(function(value) {         exec(function() { return gen.next(value); });       }, function(e) {         exec(function() { return gen.throw(e); });       });     }
      exec(function() { return gen.next(undefined); }); // 调用next方法   }); }
  | 
 
js 的异步顺序是执行完一个宏任务之后会清空微任务的队列,所以根据这个一个规则,你能说出第二段的中间两道题的输出顺序吗?
如果对本文有任何不妥之处,欢迎直接指出,交流学习 😊