async、await初步理解|8月更文挑战

210 阅读4分钟

这是我参与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,但这时等同于同步操作)
  • 返回值是 Promiseasync 函数返回值是 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
  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相比自带执行器,语义性也更好。

参考文章

理解 async/await 浅谈async/await