Promise 的方式虽然解决了 callback hell,但是这种方式充满了 Promise的 then() 方法,如果处理流程复杂的话,整段代码将充满 then,代码流程不能很好的表示执行流程。
为什么是async/await
在 es6 中,我们可以使用 Generator 函数控制流程,如下面这段代码:
function* foo(x) {
yield x + 1;
yield x + 2;
return x + 3;
}
我们可以根据不断地调用 Generator 对象的 next()
方法来控制函数的流程。但是这样仿佛不是那么的语义化。因此,在 ES6 中封装了 Generator 函数的语法糖 async 函数,但是将其定义在了 es7 中。ES7 定义出的 async
函数,终于让 JavaScript 对于异步操作有了终极解决方案。 Async
函数是 Generator 函数的语法糖。使用 关键字 Async
来表示,在函数内部使用 await 来表示异步。相较于 Generator,Async 函数的改进在于下面几点:Generator 函数的执行必须依靠执行器,而 Async()
函数自带执行器,调用方式跟普通函数的调用一样。 Async
和 await 相较于 *
和 yield
更加语义化。 async
函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then()
方法进行调用。
那么,我们通过一段小小的代码来说明 async/await 函数的用法:
未使用 async/await 的定时函数:
fn = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 2000)
})
}
const Fn = () =>{
fn().then((res) => {
console.log(res)
})
console.log(2)
}
Fn()
我相信能看到这里的各位程序员大佬应该都知道这段代码的输出状况:先打印 2,2s 之后打印出 1。
使用 async/await 的定时函数:
fn = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 2000)
})
}
const Fn = async () => {
await fn().then((res) => {
console.log(res)
})
}
Fn()
console.log(2)
这一段函数的输出状况是:2s 后打印 1,然后打印 2。
那么,why?
我们在字面上理解这两个单词 async 和 await:async 的意思是异步,async 用于定义一个异步函数,该函数返回一个 Promise。;await 的意思是等待,Promise 是一个承诺,await 也是一个承诺。Promise 的承诺是将返回值输出到 then 的回掉函数里面,无论是成功还是失败。await 的承诺是无论刮风还是下雨,我都会等你完成在做其他的步骤。因此,在上面的运用了 async/await 的代码中,会等待 fn 完全运行完成并且异步的回调完成对返回值的处理之后在开始进行下一步操作的。其原理是将异步函数转变为同步操作。
async/await 的身后事
我们说过了 async
函数返回值是 Promise 对象。
const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout));
async function f(){
await delay(1000);
await delay(2000);
await delay(3000);
return 'done';
}
f().then(v => console.log(v));// 6s之后打印'done'
那么其内部一旦抛出异常,则会导致返回的 Promise 对象状态变为 reject
状态。抛出的错误而会被 catch
方法回调函数接收到。
async function e(){
throw new Error('error');
}
e().then(v => console.log(v))
.catch( e => console.log(e));//抛出的错误会被catch捕捉到
并且,async 有一个和 promise.all 相似的特性,就是内部一点有一个 await 函数报错,后续的就不再执行了
let fn1 = ()=>{
return new Promise((resolve,reject) => {
setTimeout(()=>{
reject('故意抛出错误');
},500);
});
}
let fn2 = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1);
},500);
});
}
let getList = async ()=>{
let a = await fn1();
let b = await fn2();
return {first: a,second:b};
}
getList().then(result=> {
console.log(result);
}).catch(err=> {
console.log(err);// 由于fn1的报错,async的状态直接变成了rejected
});
当 Promise 出现的时候,我们仿佛看到了回调地狱的灭亡。当 Async/Await 出现时,异步终于不是一件困难的事情。