细说 async/await
目录
其他相关传送门
核心
1. 执行 async 函数,默认返回一个 promise 对象
2. await 相当于 promise 的 then
3. try...catch 可捕获异常,代替了 promise 的 catch
细说
1. async
async
是ES7新出的特性,表明当前函数是异步函数,不会阻塞线程导致后续代码停止运行。
async
函数,就是 Generator 函数 的语法糖。
相较于 Generator,async
函数的改进在于下面四点:
- 内置执行器:
Generator
函数的执行必须依靠执行器,而async
函数自带执行器,调用方式跟普通函数的调用一样 - 更好的语义:
async
和await
相较于*
和yield
更加语义化 - 更广的适用性:
co
模块约定,yield
命令后面只能是Thunk
函数或Promise
对象。而async
函数的await
命令后面则可以是Promise
或者 原始类型的值(Number
,string
,boolean
,但这时等同于同步操作) - 返回值是 Promise:
async
函数返回值是Promise
对象,比Generator
函数返回的Iterator
对象方便,可以直接使用then()
方法进行调用
async function asyncFn() {
console.log('这里是同步');
return '我后执行'; // 返回一个 promise 对象,相当于 return Promise.resolve('我后执行')
}
console.log(asyncFn()); // Promise {<fulfilled>: "我后执行"}
asyncFn().then(result => {
console.log(result+',这里是异步');
})
console.log('我先执行');
// 这里是同步
// 我先执行
// 我后执行,这里是异步
上面的执行结果是先打印出'我先执行'
,虽然是上面asyncFn()
先执行,但是已经被定义异步函数了,不会影响后续函数的执行。
async 定义的函数内部会默认返回一个 promise 对象:
- 如果函数内部发现不是异常或者
reject
,则判定成功,这里可以return
各种数据类型的值,false
,NaN
,undefined
...总之,都是resolve
- 如果函数内部抛出异常或者是返回
reject
,都会使函数的promise
状态为失败reject
在async
里,必须要将结果return
出去,不然的话不管是执行reject
还是resolved
的值都为undefine
//正确reject方法。必须将reject状态return出去。
async function PromiseError() {
return Promise.reject('has Promise Error');
}
//这是错误的做法,并且判定resolve,返回值为undefined,并且Uncaught报错
async function PromiseError() {
Promise.reject('这是错误的做法');
}
2. await
await
意思是 async wait
(异步等待)。
await
后面接一个会return new promise
的函数并执行它await
只能放在async
函数里
任何 async
函数都会默认返回 promise
,并且这个 promise
解析的值都将会是这个函数的返回值,而 async
函数必须等到内部所有的 await
命令的 Promise
对象执行完,才会发生状态改变。
就是说,必须等所有await
函数执行完毕后,才会告诉promise
我成功了还是失败了,执行then
或者catch
function dice(){
return new Promise((resolve, reject)=>{
let sino = parseInt(Math.random() * 6 +1);
setTimeout(()=>{
resolve(sino);
},3000);
})
}
async function test(){
let n =await dice(); // await 相当于 Promise 的 then
console.log(n);
}
test();
把await
和成功后的操作放到try
里,失败的放在catch
:
function dice(val) {
return new Promise((resolve, reject) => {
let sino = parseInt(Math.random() * 6 + 1);
if (sino > 3) {
val === '大' ? resolve(sino) : reject(sino);
} else {
val === '大' ? reject(sino) : resolve(sino);
}
})
}
async function test() {
// try...catch 可捕获异常,代替了 Promise 的 catch
try {
//把await及获取它的值的操作放在try里
let n = await dice('大'); // await 相当于 Promise 的 then
console.log('赢了' + n);
} catch (error) {
//失败的操作放在catch里
console.log('输了' + error); // 相当于 Promise 的 catch
}
}
test();
await 后面是 rejected 状态的栗子:
(async function () {
const p = Promise.reject('promise error');
const res = await p; // await 相当于 Promise 的 then, 但这里是rejected状态,会报错
console.log(res);
})()
用 try...catch
捕获异常即可:
(async function () {
const p = Promise.reject('promise error');
try {
const res = await p;
console.log(res);
} catch (error) {
console.log(error); // 正常输出: promise error
}
})()
3. await 等到之后,做了一件什么事情?
await 下面所有的代码都是异步
await
等到的结果分2种:
- 不是promise对象
如果不是 promise , await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完,再回到async内部,把这个非promise的对象,作为 await表达式的结果。
async function async1() {
await async2(); // 先执行async2(),await下面所有的代码都是异步
console.log(1); // 异步
}
async function async2() {
console.log(2);
}
console.log(3);
async1();
// 3
// 2
// 1
- 是promise对象
如果是 promise 对象,await 也会阻塞async后面的代码,先执行async外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。
function fn() {
return new Promise(resolve => {
console.log(1); // 同步2
resolve();
})
}
async function async1() {
await fn().then(() => { // 先执行fn(),await下面所有的代码都是异步
console.log(2); // 异步1
})
console.log(3); // 异步1的下一个异步
}
console.log(4); // 同步1
async1();
// 4
// 1
// 2
// 3
如果asycn里的代码都是同步的,那么这个函数被调用就会同步执行
async function fn(){
console.log('a'); // 同步1
}
fn();
console.log('b'); // 同步2
//a
//b
async function async1() {
console.log(1); // 同步2
await async2(); // 先执行async2() 再await
console.log(2); // 异步(await下面所有的代码都是异步)
}
async function async2() {
console.log(3); // 同步3
}
console.log(4); // 同步1
async1();
console.log(5); // 同步4
// 4
// 1
// 3
// 5
// 2
async function async1() {
console.log(1);
await async2();
console.log(2);
await async3();
console.log(3);
}
async function async2() {
return new Promise((resolve) => {
console.log(4);
resolve(); // 如果没有resolve(),await async2()后的代码都不会执行,只输出6 1 4 7
})
}
async function async3() {
console.log(5);
}
console.log(6);
async1();
console.log(7);
// 6
// 1
// 4
// 7
// 2
// 5
// 3
4. 使用 async/await 改写 then 链
相比于 Promise
,async/await
能更好地处理 then链
function takeLongTime(n) {
return new Promise(resolve => {
setTimeout(() => resolve(n + 200), n);
});
}
function step1(n) {
console.log(`step1 with ${n}`);
return takeLongTime(n);
}
function step2(n) {
console.log(`step2 with ${n}`);
return takeLongTime(n);
}
function step3(n) {
console.log(`step3 with ${n}`);
return takeLongTime(n);
}
使用then的链式调用
:
function doIt() {
console.time("doIt");
const time1 = 300;
step1(time1)
.then(time2 => step2(time2))
.then(time3 => step3(time3))
.then(result => {
console.log(`result is ${result}`);
});
}
doIt();
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
使用 async/await
:
async function doIt() {
console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time2);
const result = await step3(time3);
console.log(`result is ${result}`);
}
doIt();
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900