async/await 是一个用同步的思维来解决异步问题的方案。
async 函数就是 Generator 函数的语法糖。
async 函数的优点
async写在function前面,该函数返回值是一个promise,可以直接使用then方法。
async function test() {
return "test";
}
test().then(function(v) {
console.log(v);
}); //“test”
await只能在async函数中使用。
async function test() {
return "test";
}
var v = await test();
console.log(v); //“test”
await不能工作在顶级作用域,需要将await代码包裹在一个async函数中
await接受thenables
就像promise.then,await也允许使用thenable对象(那些具有可调用的then方法的对象)。同样,第三方对象可能不是一个promise,但是promise的兼容性表示,如果它支持.then方法,那么它就能用于await。
例如,这里await接受了new Thenable(1)
class Test {
constructor(str) {
this.str= str;
}
then(resolve, reject) {
setTimeout(() => {
resolve(this.str);
}, 1000);
}
}
async function fun() {
let result = await new Test("abc");
console.log(result);
}
fun();
如果await得到了一个带有then方法的非promise对象,它将会调用提供原生函数resolve、reject作为参数的方法,然后await一直等待,直到他们其中的一个被调用。
async方法
一个class方法同样能够使用async,只需要将async放在它之前就可以
就像这样:
class Waiter {
async wait () {
return await Promise.resolve(1)
}
}
new Waiter().wait().then(alert) // 1
这里的意思是一样的:它确保了返回值是一个promise,支持await
async、await串行并行处理
串行:等待前面一个await执行后接着执行下一个await,以此类推
async function test(str) {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(str);
}, 1000);
})
}
var fun = async () => { //串行执行
console.time('fun');
console.log(await test('async 1'));
console.log(await test('async 2'));
console.log(await test('async 3'));
console.timeEnd('fun');
}
fun();
可以看到两个await串行执行的总耗时为两千多毫秒。
并行:将多个promise直接发起请求(先执行async所在函数),然后再进行await操作。
async function test(str) {
return await new Promise((resolve, reject) => {
setTimeout(() => {
resolve(str);
}, 1000);
})
}
var fun1 = async () => { //并行执行
console.time('fun');
const t1 = test('async 1');
const t2 = test('async 2');
const t3 = test('async 3');
//直接打印
console.log(await t1);
console.log(await t2);
console.log(await t3);
console.timeEnd('fun');
}
var fun2 = async () => { //串行执行
console.time('fun2');
const t1 = test('async 1');
const t2 = test('async 2');
const t3 = test('async 3');
const t = [t1, t2, t3];
console.log(await Promise.all(t));
console.timeEnd('fun2');
}
fun1();
fun2();
fun1与fun2写法效果基本无异
通过打印我们可以看到相对于串行执行,效率提升了一倍。在并行请求中先执行async的异步操作再await它的结果,把多个串行请求改为并行可以将代码执行得更快,效率更高。
错误处理
可以使用try-catch语句捕获错误,就像在正常抛出中处理异常一样。
如果我们不使用try-catch,然后async函数的调用产生的promise变成reject状态的话,我们可以添加.catch去处理它:
如果我们忘记添加.catch,我们就会得到一个未被处理的promise错误(能够在控制台里看到它),这时我们可以通过使用一个全局的事件处理器去捕获错误。
async function myFun() {
try {
await fun();
} catch (err) {
console.log(err);
}
}
// 另一种写法
async function myFun() {
await fun().catch(function (err){
console.log(err);
});
}
总结
放在一个函数前的async有两个作用:
1)使函数总是返回一个promise
2)允许在这其中使用await
有了async/await,很少需要写promise.then/catch,但是Promise.all没有替代方式,它能够同时等待很多任务。
async/await对比Promise优点:
1) 真正地用同步的方式写异步代码
2) 不用写then及其回调函数,减少代码行数,也避免了代码嵌套
3) 所有异步调用可以写在同一个代码块中,无需定义多余的中间变量
4) async函数会隐式地返回一个Promise,因此可以直接return变量,无需使用Promise.resolve进行转换