ES8提出的async 和 await 关键字两种语法结合可以让异步代码像同步代码一样,可以解决promise对象不断使用then方法的代码不简洁问题。
async、await 语法是ES8(ECMAScript 2017)里面的新语法,在2016年被提出,它的作用就是 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。它可以很好的替代promise 中的then。async 函数返回一个 Promise 对象,可以使用then 方法添加回调函数。当函数执行的时候,一旦遇到await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
在async/await之前,我们有如下三种方式写异步代码:
一、嵌套回调
二、以Promise为主的链式回调
三、使用Generators
但是,这三种写起来都不够优雅,ES8做了优化改进,async/await应运而生,async/await相比较Promise 对象then函数的嵌套,与 Generator 执行的繁琐(需要借助第三方库co才能自动执行,否则得手动调用next() ), Async/Await 可以让你轻松写出同步风格的代码同时又拥有异步机制,更加简洁,逻辑更加清晰。
async 和 await 是 ES2017 新增两个关键字,它们借鉴了 ES2015 中生成器在实际开发中的应用,目的是简化 Promise api 的使用,并非是替代 Promise。
async/await的特点
1. async/await更加语义化,async 是“异步”的简写,async function 用于声明一个 function 是异步的; await,可以认为是async wait的简写, 用于等待一个异步方法执行完成;
2. async/await是一个用同步思维解决异步问题的方案(等结果出来之后,代码才会继续往下执行)
3. 可以通过多层 async function 的同步写法代替传统的callback嵌套
async/awiat的使用规则
1. async 返回的是一个 Promise 成功的对象,await 就是等待这个 promise 的返回结果后,再继续执行。
2. await 等待的是一个 Promise 对象,后面必须跟一个 Promise 对象,但是不必写 then (),直接就可以得到返回值。
但注意,await 所等待的 promise 对象,他的最终状态一定是 resolve 的(当然也可以不是 resolve ,只不过不会执行后面的代码罢了),否则不会执行await 后面的代码,也就是不会去执行所谓的 then() ;
async function 语法
目的是简化在函数的返回值中对Promise的创建。async 用于修饰函数(无论是函数字面量还是函数表达式),放置在函数最开始的位置,被修饰函数的返回结果一定是 Promise 对象。
1、自动将常规函数转换成Promise,返回值也是一个Promise对象
2、只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
3、异步函数内部可以使用await
async function name([param[, param[, ... param]]]) { statements }
name: 函数名称。
param: 要传递给函数的参数的名称
statements: 函数体语句。
返回值:返回的Promise对象会以async function的返回值进行解析,或者以该函数抛出的异常进行回绝。
async function add1(x) {// async声明add1是一个异步函数let a = 1;return x + a;
}add1(10); // 执行add1函数,传递参数10
执行结果,如下:
可以看出add1(10)的返回值是一个resolved的Promise
await 语法
await关键字必须出现在async函数中!!!!await用在某个表达式之前,如果表达式是一个Promise,则得到的是thenable中的状态数据。
1、await 放置在Promise调用之前,await 强制后面的代码等待,直到Promise对象resolve,得到resolve的值作为await表达式的运算结果。
2、await只能在async函数内部使用,用在普通函数里就会报错
[return_value] = await expression;
expression: 一个 Promise 对象或者任何要等待的值。
返回值:返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。
function pm() {// pm函数返回一个promise对象return new Promise((resolve, reject) => {resolve("promise value:1");})
};
async function test() {let a = await pm(); // 等待一promise对象,返回promise对象的处理结果 (这里是resolve的'promise value:1')let b = await "not promise value: 2"; // 等待一个字符串,立即返回该值本身console.log(a); // 打印 a的值console.log(b); // 打印 b 的值return a + b;
}
在开发者工具里,测试执行结果,如下:
async/awiat的错误处理
在async函数里,无论是Promise reject的数据还是逻辑报错,都会被默默吞掉,所以最好把await放入try{}...catch{}中,catch能够捕捉到Promise对象rejected的数据或者抛出的异常
function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(() => {reject("error");}, ms); //reject模拟出错,返回error});
}
async function asyncPrint(ms) {try {console.log("start");await timeout(ms); //这里返回了错误console.log("end"); //所以这句代码不会被执行了} catch (err) {console.log(err); //这里捕捉到错误error}
}
asyncPrint(1000);
如果不用try / catch的话,也可以像下面这样处理错误(因为async函数执行后返回一个promise)
function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(() => { reject('error') }, ms); //reject模拟出错,返回error});
}
async function asyncPrint(ms) {console.log('start');await timeout(ms)console.log('end'); //这句代码不会被执行了
}
asyncPrint(1000).catch(err => {console.log(err); // 从这里捕捉到错误
});
如果你不想让错误中断后面代码的执行,可以提前截留住错误,像下面
function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(() => {reject("error");}, ms); //reject模拟出错,返回error});
}
async function asyncPrint(ms) {console.log("start");await timeout(ms).catch((err) => {// 注意要用catchconsole.log(err);});console.log("end"); //这句代码会被执行
}
asyncPrint(1000);
async/awiat的使用场景
多个await命令的异步操作,如果不存在依赖关系(后面的await不依赖前一个await返回的结果),用Promise.all()让它们同时触发
function test1() {return new Promise((resolve, reject) => {setTimeout(() => {resolve(1);}, 1000);});
}function test2() {return new Promise((resolve, reject) => {setTimeout(() => {resolve(2);}, 2000);});
}async function exc1() {console.log("exc1 start:", Date.now());let res1 = await test1();let res2 = await test2(); // 不依赖 res1 的值console.log("exc1 end:", Date.now());
}async function exc2() {console.log("exc2 start:", Date.now());let [res1, res2] = await Promise.all([test1(), test2()]);console.log("exc2 end:", Date.now());
}exc1();exc2();
exc1 的两个并列await的写法,比较耗时,只有test1执行完了才会执行test2。你可以在浏览器的Console里尝试一下,会发现exc2的用Promise.all执行更快一些
await返回打印测试
await后面的promise状态不是resolve的输出结果
async function async1() {console.log("async1 start");await new Promise((resolve) => {console.log("promise1");});console.log("async1 success");return "async1 end";
}
console.log("srcipt start");
async1().then((res) => console.log(res));
console.log("srcipt end");/* await后面的promise状态不是resolve的输出结果
"srcipt start"
"async1 start"
"promise1"
"srcipt end"
*/
在开发者工具里,测试执行结果,如下:
这里我们可以看到:在 async1 中 await 后面的 Promise 是没有返回值的,也就是它的状态始终是 pending 状态,所以在 await 之后的内容是不会执行的,包括 async1 后面的 .then。
await后面的promise状态是resolve的输出结果
async function async1() {console.log("async1 start");await new Promise((resolve) => {console.log("promise1");resolve();});console.log("async1 success");return "async1 end";
}
console.log("srcipt start");
async1().then((res) => console.log(res));
console.log("srcipt end");/*await后面的promise状态是resolve的输出结果
"srcipt start"
"async1 start"
"promise1"
"srcipt end"
"async1 success"
"async1 end"
*/
在开发者工具里,测试执行结果,如下:
async/await的兼容性
在自己的项目中使用
通过 babel 来使用。只需要设置 presets 为 stage-3 即可。
async/await的总结
解决函数回调经历了几个阶段, Promise 对象, Generator 函数到async函数。async函数目前是解决函数回调的最佳方案。很多语言目前都实现了async,包括Python ,java spring,go等。
async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
小结:async 和 await是ES2017(即ES8)提出的前端异步编程特性。
在ES7(ECMAScript 2017)中,async/await被引入,这是一个重要的前端异步编程特性,使得异步代码的书写和理解更加直观和易于管理,几乎让异步代码看起来像同步代码一样简单。这个特性通过Promise对象来实现,允许你以同步的方式编写异步代码,从而提高代码的可读性和可维护性。在ES8中,async/await的引入,使得处理异步操作变得更加简单和直观,减少了回调嵌套的复杂性,提高了代码的质量和开发效率。尽管async/await是在ES8中正式提出,但它们的使用已经广泛影响了后续的ES版本和JavaScript的开发实践。
此外,ES9引入了for await...of循环,这是一个专门用于处理异步迭代器的语法,进一步扩展了async/await的功能和应用场景。for await...of循环允许开发者更方便地处理异步数据流,如读取文件、网络请求等返回的数据流,使得异步编程的模型更加完善和灵活。
综上所述,async和await作为前端异步编程的重要特性,虽然在ES7(ES2017)中被引入,但它们的影响力和应用在ES8及以后的版本中得到了进一步的扩展和完善
参考资料
—— JS | Promise对象的使用详解 | JS | ES6新增对象 - Promise使用方法详解 - CSDN博客 ——
—— JS | ES6 新增函数类型——Generator 函数使用方法详解-CSDN博客 ——
ES6-Promise简介、ES7 新特性及ES8新特性async和await -CSDN博客
es7中的async、await使用方法示例详解_await request-CSDN博客
async与await总结 | async和await - 简书 | ES6—async和await - CSDN博客
async和await - CSDN博客 | 深入理解ES7的async/await - CSDN博客