async/await 异步编程终极解决方案

258 阅读3分钟

“这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

与generater的差异

更好的语义

async和await,比起*和yield,语义更清楚。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果

内置执行器

Generator函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。

更广的适用性

co模块约定,yield命令后面只能是Thunk 函数或Promise对象,而async函数的await命令后面,可以是 Promise对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即resolved的 Promise对象)。

返回值是Promise

async函数的返回值是Promise对象,这比 Generator函数的返回值是Iterator对象方便多了。你可以用then方法指定下一步的操作。

例子

当执行到asyncPrint("hello world", 1000);时,遇到了await,这代表了此处99%有异步代码,就会让出控制权,继续执行主线程后面的代码console.log("hello async");,执行栈空了,就会去查看消息(任务)队列(包含微任务和宏任务),先执行全局微任务,微任务执行完毕后,执行宏任务。微任务await timeout(ms);压入执行栈执行,此处会等待1秒(因为要等new Promise((resolve) => { setTimeout(resolve, ms) })返回promise状态也就是执行完resolve()),await执行完毕后,让出控制权,继续执行async中的同步代码,执行 console.log(value);

    return new Promise((resolve) => {
        setTimeout(resolve, ms)
    })
}

async function asyncPrint(value, ms) {
    await timeout(ms);
    console.log(value);
}

asyncPrint("hello world", 1000);
console.log("hello async");

//输出顺序
// hello async
// hello world

当执行到asyncPrint("hello world", 1000);时,遇到了await,这代表了此处99%有异步代码,就会让出控制权,继续执行主线程后面的代码console.log("hello async");,执行栈空了,执行await setTimeout(function() { console.log(1122) }, 0);,将宏任务setTimeout交给宏任务队列,继续执行async中的同步代码console.log(value);,执行await 2,直接将2放到微任务中;继续执行async中的同步代码console.log(3344);。同步代码执行完毕,执行微任务,微任务清空了,执行宏任务 console.log(1122) 。

function timeout(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms)
    })
}

async function asyncPrint(value, ms) {
    await setTimeout(function() { console.log(1122) }, 0);
    console.log(value);
    await 2;
    console.log(3344);
}

asyncPrint("hello world", 1000);
console.log("hello async");

//输出顺序
// hello async
// hello world
// 3344
// 1122

async注意事项

  • await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。
  • 多个await命令后面的异步操作,如果不存在继发(依赖)关系,最好让它们同时触发。
  • 第三点,await命令只能用在async函数之中,如果用在普通函数,就会报错。