不懂async?撸Demo啊

584 阅读7分钟

什么是async?

async 函数是 Generator 函数的语法糖。使用 关键字 async 来表示,在函数内部使用 await 来表示异步 ,async 函数返回值是 Promise 对象

如果async关键字函数 return 的不是promise,会自动用Promise.resolve()包装 如果async关键字函数显式地 return 一个 promise,那就以你返回的promise为准

  • async与普通函数
async function fn1(){
    return 123
}

function fn2(){
    return 123
}

console.log(fn1())
console.log(fn2())

// 输出
Promise {<resolved>: 123}
123

  • async返回promise
async function asyncFn() {
    return '1'
}
asyncFn().then(result => {
    console.log(result);
})
console.log('2');

// 输出
2 1

失败

async定义的函数内部会默认返回一个promise对象,但是返回如下结果会使async函数判定失败reject

  1. 内部含有直接使用并且未声明的变量或者函数。
  2. 内部抛出一个错误throw new Error或者返回reject状态 return Promise.reject('执行失败')
  3. 函数方法执行出错(Object使用push())等

await是什么?

await 只能在使用async定义的函数里面使用。async函数必须等到内部所有的 await 命令的 Promise 对象执行完,才会发生状态改变。 正常情况下,await 命令后面跟着的是 Promise ,如果不是的话,也会被转换成一个 立即 resolve 的 Promise

await 在等什么

await等的是右侧「表达式」的结果
在等到结果之前,await 会阻塞后面的代码
但是,async函数中若某个awaitPromise 对象状态变为rejected,后续代码不会执行

async function asyncFn() {
  await Promise.reject("1");
  console.log("2");  //  不会执行
}

asyncFn()
  .then(result => {
    console.log(result);
  })
  .catch(error => console.log(error));

console.log('3');

// 输出 3 1

await 执行顺序

await是从右往左执行的
await是从右往左执行的
await是从右往左执行的

重要的事情说三遍,这个对于细节理解很重要

什么时候产生阻塞

在遇到 await 的时候,才会发生阻塞. 但由于是从右往左,所以

await 右侧的代码会先执行
await 右侧的代码会先执行
await 右侧的代码会先执行

右侧先执行,因为前面有 await,所以会阻塞执行结果,将结果放入微任务中!

阻塞什么时候恢复

这要取决于 await 右侧的执行结果是什么

  • promise对象
  • 非promise对象
  1. 如果 await 等到的是一个 promise 对象,await 也会暂停 async 后面的代码,先执行 async 外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。

  2. 如果不是 promise , await 会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完,再回到 async 内部,把这个非promise的东西,作为 await 表达式的结果

  • await与非promise
async function asyncFn() {
  await "1";
  console.log("2");
}

asyncFn()
  .then(result => {
    console.log(result);
  })

console.log('3');

// 输出3 2 undefined

分析

  1. 开始执行 asyncFn(),从右到左执行到await "1",此时 "1" 等价于 Promise.resolve("1"),所以函数相当于:
async function asyncFn() {
  await Promise.resolve("1");
  console.log("2");
}

asyncFn()
  .then(result => {
    console.log(result);
  })
console.log('3');

因为从右到左执行,所以先执行 Promise.resolve("1"),该 Promise 状态变为 fulfilled

  1. 执行到 await,阻塞 async 后面的代码,跳到外层执行同步代码,输出 3

  2. 同步代码执行完毕,微任务里没有任务,检查 await,发现右侧是一个已经 fulfilled 的 Promise(fulfilled),所以取消阻塞,继续向下执行,输出 2

  3. 继续执行,asyncFn执行结束,没有返回值,相当于 返回了一个 Promise.resolve(),所以 asyncFn() 其后的 then(result => { console.log(result); })压入微任务栈

  4. 继续执行,发现没有同步代码了, 轮询微任务队列,执行console.log(result);输出 undefined

  • async 与 promise 将上述例子稍微变动一下,加个promise看看
async function asyncFn() {
  await "1";
  console.log("2");
}

asyncFn()
  .then(result => {
    console.log(result);
  })

new Promise((resolve, reject) => {
  resolve(4);
}).then(data => {
    console.log(data);
})

console.log('3');

// 输出 3 2 4 undefined

分析

  1. 开始执行 asyncFn(),从右到左执行到await "1",此时阻塞 async 后的代码执行

  2. 跳到外层执行同步代码,执行resolve(4),其后的 then(data => { console.log(data); }) 被压入微任务栈

  3. 继续执行同步代码,输出3

  4. 回到 await,取消其阻塞,执行async 后面代码,输出2

  5. 轮询微任务队列,里面有两个微任务,按照顺序执行,输出 4,随后输出 undefined,注意这里不是输出两个 undefined

  • async 与 微任务栈 下面的例子,与微任务栈的理解强相关。希望读者先有一个准确的概念

image.png

当你在某次微任务中,引发了新的微任务,这个微任务虽然是被压入了微任务栈,但它与当前的微任务并不在同一次轮询当中

async function asyncFn() {
  await func();
  console.log("asyncFn1");
  return "asyncFn2"
}

function func(){
    console.log("asyncFn3");
    return new Promise((res,rej)=>{
             res('asyncFn4');
           })
           .then(data => {console.log(data); return 'asyncFn5'})
           .then(data => {console.log(data)})
}

asyncFn()
  .then(data => {
    console.log(data);
  })

new Promise((resolve, reject) => {
  resolve('promise1');
}).then(data => {
    console.log(data);
    return 'promise2'
}).then(data => {
    console.log(data);
    return 'promise3'
}).then(data => {
    console.log(data)
})

// 输出
asyncFn3
asyncFn4
promise1
asyncFn5
promise2
asyncFn1
promise3
asyncFn2

分析

  1. 开始执行 asyncFn(),从右到左执行到await func();,执行 func,输出asyncFn3
  2. 继续执行, return promise,此时继续执行promise,res('asyncFn4') 执行,该promise状态变为 resolved,其回调 .then 压入微任务栈(微任务1),func()执行结束
  3. await 阻塞 async 后的代码执行,跳到外层执行同步代码,执行 resolve('promise1');,其回调.then压入微任务栈(微任务2)
  4. 同步代码执行完毕,轮询微任务栈
  5. 微任务1 输出asyncFn4,其回调then 压入微任务栈(微任务3)
  6. 微任务2 输出promise1,其回调.then 压入微任务栈栈(微任务4)
  7. 第一轮微任务轮询结束,检查同步代码,没有需要执行的,继续轮询微任务栈
  8. 微任务3 输出asyncFn5
  9. 微任务4 输出promise2,其回调.then 压入微任务栈(微任务5)
  10. 第二轮微任务轮询结束,检查同步代码,await 等待的promise状态已经变成 resolved,变为非阻塞状态,执行同步代码,输出asyncFn1,asyncFn 状态变为 resolved,其回调 .then 压入微任务栈(微任务6)
  11. 同步任务结束,微任务5,输出promise3
  12. 微任务6,输出asyncFn2
  • async 与 宏任务
async function async1() {
    console.log("async1 start");
    await async2();
    console.log("async1 end");
}

async function async2() {
    console.log("async2");
}

setTimeout(function() {
    console.log("setTimeout");
}, 0);

async1();

new Promise(function(resolve) {
    console.log("promise1");
    resolve();
}).then(function() {
    console.log("promise2");
});

// 输出
async1 start
async2
promise1
async1 end
promise2
setTimeout

魔鬼训练

const timeoutFn = function(timeout){
	return new Promise(function(resolve){
		         return setTimeout(resolve, timeout);
           });
}

async function fn(){
    await timeoutFn(1000);
    await timeoutFn(2000);
    return '完成';
}

fn().then(success => console.log(success));
console.log('2');

// 先输出2,3s后输出完成
const timeoutFn = function(timeout){
	return new Promise(function(resolve){
		         return setTimeout(resolve, timeout);
           });
}

const timeoutFn2 = function(timeout){
	return new Promise(function(resolve){
		         return setTimeout(console.log(timeout), timeout);
           });
}

async function fn(){
    await timeoutFn(1000);
    await timeoutFn2(2000);
    return '完成';
}

fn().then(success => console.log(success));
console.log('2');

// 先输出2,1s后输出2000,由于timeoutFn2状态一直未变成fulfilled,所以不会输出完成
const timeoutFn = function(timeout){
  console.log(timeout);
  return new Promise(function(resolve){
            return setTimeout(()=>{
                     console.log("resolve in")
                     resolve();
                   }, timeout);
          });
}

async function fn(){
    await timeoutFn(1000);
    console.log("第一个完成");
    await timeoutFn(2000);
    return '完成';
}

fn().then(success => console.log(success));

// 输出1000,等待1s,输出resolve,第一个完成,2000,等待2s,输出resolve,完成
function timeout(time){
    return new Promise(function(resolve){
        return setTimeout(function(){ 
                    return resolve(time + 1000)
               },time);
    })
}

function first(time){
   console.log('第一次延迟了' + time );
    return timeout(time);
}

function second(time){
    console.log('第二次延迟了' + time );
    return timeout(time);
}

function third(time){
    console.log('第三次延迟了' + time );
    return timeout(time);
}

async function start() {
    console.log('START');
    const time1 = 1000;
    const time2 = await first(time1);
    const time3 = await second(time2);
    const res = await third(time3);
    console.log(`最后一次延迟${res}`);
}

start();
async function testSometing() {
    console.log("testSomething");
    return "return testSomething";
}

async function testAsync() {
    console.log("testAsync");
    return Promise.resolve("hello async");
}

async function test() {
    console.log("test start...");

    const testFn1 = await testSometing();
    console.log(testFn1);
    
    const testFn2 = await testAsync();
    console.log(testFn2);
    
    console.log('test end...');
}

test();

var promiseFn = new Promise((resolve)=> { 
                    console.log("promise START...");
                    resolve("promise RESOLVE");
                });
promiseFn.then((val)=> console.log(val));

console.log("===END===")

//输出
test start...
testSomething
promise START...
===END===
return testSomething
testAsync
promise RESOLVE
hello async
test end...