“这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战”
前言
ES6引入的promise虽然可以较好的解决回调地狱的问题,但是then()方法的本质依然是回调函数,并且promise还是比较难理解掌握的,在很多人学习promise的过程中,我们很容易产生这样的想法,promise太难了,我宁愿面对回调地狱,也不愿意去学习promise, 这就说明,为了解决回调函数这个难题,引入了更加复杂的promise语法,所以基于这个原因,ES7又引进了async/await,作为异步编程的更好的解决方案,真正的可以用同步的方式书写异步的编程逻辑,async/await看上去是同步的,但是在后台执行的时候却是异步非阻塞的。当然需要注意的是async/await的实现是基于promise和Generator的。
async、await与Generator的联系及优势
- 内置执行器。Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样。完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。
- 更好的语义。async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
- 更广的适用性。
co模块约定,yield命令后面只能是 Thunk 函数或 Promise对象。而async函数的await命令后面则可以是 Promise 或者 原始类型的值(Number,string,boolean,但这时等同于同步操作) - 返回值是 Promise。
async函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用then()方法进行调用。 进一步说,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。
async关键字
1.async关键字一般不单独使用,而是和await一起使用,一个async函数内部可能有一个或者多个await 。async函数返回一个promise对象,可以使用then方法添加回调函数。
async function test() {
return '111'
};
test().then( (v) => console.log(v)) //111
2.串行与并行,async 函数返回的 Promise 对象,必须等到内部所有的 await 命令的 Promise 对象执行完,才会发生状态改变
也就是说,只有当 async 函数内部的异步操作全部都执行完,才会执行 then 方法的回调。
const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout));
async function f(){
await delay(2000);
await delay(3000);
await delay3000);
return 'ok';
}
f().then(v => console.log(v)); // 等待8s后才输出 'ok'
对于这点,在实际应用中一定要注意,假设有三个ajax请求,这种写法的结果是8s之后,才拿到完成请求,显然这是不能接受的。 如果要同时并行执行,正确的做法应该是使用promise.all()方法:
const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout));
async function f(){
let p1 = delay(2000);
let p2 = delay(3000);
let p3 = delay(3000);
await Promise.all([p1, p2, p3]);
return 'ok';
}
f().then(v => console.log(v)); // 等待3s后才输出 'ok'
正常情况下,await 命令后面跟着的是 Promise ,如果不是的话,也会被转换成一个立即 resolve 的 Promise 如下面这个例子:
async function f() {
return await 1
};
f().then( (v) => console.log(v)) // 1
- 错误处理 可以使用try/catch的方式来处理错误,相比promise的.catchf()处理错误的方式更加的灵活。
function sleep(second) {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('want to sleep~');
}, second);
})
}
async function errorDemo() {
let result = await sleep(1000);
console.log(result);
}
errorDemo();// VM706:11 Uncaught (in promise) want to sleep~
// 为了处理Promise.reject 的情况我们应该将代码块用 try catch 包裹一下
async function errorDemoSuper() {
try {
let result = await sleep(1000);
console.log(result);
} catch (err) {
console.log(err);
}
}
errorDemoSuper();// want to sleep~
// 有了 try catch 之后我们就能够拿到 Promise.reject 回来的数据了。
总结
promise语法比较难以理解,为了解决异步编程的难题,引入了更为复杂的promise语法, 所以promise不是异步编程的最终解决方案。Generator虽然实现了以同步代码实现异步编程,但是必须依靠执行器,每次都需要通过next()的方式去执行。async、await语法简单,以同步代码实现异步编程,表面看上去是同步的,后台执行却是异步非阻塞的,而且和Generator相比自带执行器,语义性也更好。