async和promise

200 阅读3分钟

当async、await、promise、promise.then同时存在时,代码该如何执行?

例子一

async function async1() {
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
    
async function async2() {
    console.log('async2')
}
    
console.log('script start')

setTimeout(function () {
    console.log('setTimeout')
}, 0)
    
async1();
    
new Promise(function (resolve) {
    console.log('promise1')
    resolve()
}).then(function () {
    console.log('promise2')
})
    
console.log('script end')

执行过程 顺序执行,log出script start,注册一个宏任务setTimeout

执行async1

执行async1中的log

执行await后的async2

这里需要注意,async2虽然是异步函数,但是在遇到await之前,代码是同步执行的,所以这里会log出async2,执行完之后,没有return,所以返回undefined。此时代码变成了promise.resolve(undefined)。

async function async1() {
    console.log('async1 start')
    await Promise.resolve(undefined)
    console.log('async1 end')
}

await后的函数相当于在promise.then()中,执行,所以现在注册了一个微任务

代码如下

async function async1() {
    console.log('async1 start')
    var promise = await Promise.resolve(undefined)
    promise.then(() => {
     console.log('async1 end')
    })
}

接着执行同步代码,new Promise中log出promise1,然后注册一个微任务

接着执行同步代码,log出script end,同步代码执行完毕,开始执行微任务

执行第一个微任务,log出async1 end

执行第二个微任务,log出promise2

执行宏任务,log出setTimeout

综上,执行结果是

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

上面代码改为promise为

function async1() {
  console.log('async1 start')
  new Promise((resolve) => {
    console.log('async2');
    resolve()
  }).then(() => {
    console.log('async1 end')
  })
}
  
console.log('script start')

setTimeout(function () {
    console.log('setTimeout')
}, 0)
    
async1();
    
new Promise(function (resolve) {
    console.log('promise1')
    resolve()
}).then(function () {
    console.log('promise2')
})
    
console.log('script end')

总结:

new Promise内的代码是同步代码,promise.then()中的代码是异步代码中的微任务,setTimeOut是异步代码中的宏任务,而代码的执行顺序是同步代码 -> 微任务 -> 宏任务。 在async函数中,await之前的代码是同步代码,跟随await的是异步代码,await之后的代码在await返回resolved的状态后才会执行。

小提醒:如果await返回一个promise,只有当promise是resolved的状态时,才会执行后面的代码,如果await后面不是一个promise,会把当前值转化为已经解决的promise,继续执行后面的代码,并且等待处理结果。

async function async1() {
    console.log('async1 start')
    await promise1()
    console.log('async1 end')
}
    
function promise1() {
  return new Promise(() => { // 注意return
   console.log('promise');
  })
}

如果有return的话,返回的promise是pending状态,后面的console.log就不会执行,没有return的话,相当于返回 undefined,await会包裹一层Promise.resolve(),所以console.log会执行。

例子二

setTimeout(_ => console.log(4))

new Promise(resolve => {
  resolve()
  console.log(1)
}).then(_ => {
  console.log(3)
})

console.log(2)

这个例子比较简单

顺序执行,注册一个宏任务

执行new Promise,log出1,同时注册一个微任务

执行同步代码,log出2

执行微任务,log出3

执行宏任务,log出4

所以顺序是

1
2
3
4

后续: 虽然已经做了重点标记,但再次看的时候,还是在async2的地方出了错。原因在于不知道生成器函数在调用next之后,暂停在了哪里,是yield之前?yield中?还是yield后?

经过测试和官网文档,可以确定,在调用next时,会执行yield后面的表达式,执行完后,会暂停

截屏2021-04-1113.32.09.png image.png

测试用例

function* gen() {
 yield new Promise((resolve) => {
  console.log('next');
  setTimeout(() => resolve(1))
});
 console.log('after');
}
var v = gen();

第一次调用next

截屏2021-04-1113.37.38.png

第二次调用next

截屏2021-04-1113.35.53.png