我们先看一个需求,然后再一步步推导出async、await的原理
需求:
代码示例:
有三种解决方案:
方案一:多次回调,会造成回调地狱
requestData('zyk').then(res => {
requestData(res + 'aaa').then(res => {
requestData(res + 'bbb').then(res => {
requestData(res + 'ccc').then(res => {
console.log(res);
})
})
})
})
方案二:返回一个新的promise,它的resolve决定下一个then的回调函数的值
requestData('zyk').then(res => {
return requestData(res + 'aaa')
}).then(res => {
return requestData(res + 'bbb')
}).then(res => {
return requestData(res + 'ccc')
}).then(res => {
console.log(res);
})
方案三:Promise + Generator的结合使用
function* getData() {
const res1 = yield requestData('zyk')
const res2 = yield requestData(res1 + 'aaa')
const res3 = yield requestData(res2 + 'bbb')
const res4 = yield requestData(res3 + 'ccc')
console.log(res4);
}
const generator = getData()
generator.next().value.then(res => {
generator.next(res).value.then(res => {
generator.next(res).value.then(res => {
generator.next(res).value.then(res => {
console.log(res);
})
})
})
})
方案四:相当于是Promise + generator的语法糖
async function getData() {
const res1 = await requestData('zyk')
const res2 = await requestData(res1 + 'aaa')
const res3 = await requestData(res2 + 'bbb')
const res4 = await requestData(res3 + 'ccc')
console.log(res4);
}
getData()
async关键字用于声明一个异步函数
执行流程:
异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行
2.返回值与普通函数的区别
异步函数的返回值一定是一个promise
区别:
情况一:异步函数也可以有返回值,但是异步函数的返回值会被包裹到Promise.resolve中;
情况二:如果我们的异步函数的返回值是Promise,Promise.resolve的状态会由Promise决定;
情况三:如果我们的异步函数的返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定;
代码示例:
async function foo() {
console.log("start");
console.log("执行代码");
console.log("end");
// 1.返回普通值
// return 'aaa'
// 2.是一个对象并且实现了thenable
// return {
// then: function(resolve) {
// resolve('bbb')
// }
// }
// 返回的是一个promise
return new Promise((resolve, reject) => {
resolve('ccc')
})
}
// 异步函数的返回值一定是个promise
const promise = foo()
promise.then(res => {
console.log("执行代码:", res);
})
处理异常:
如果我们在async中抛出了异常,那么程序它并不会像普通函数一样报错,而是会作为Promise的reject来传递;
async function foo() {
console.log("start");
console.log("执行代码");
// 异步函数中的异常 会被作为异步函数返回Promise的reject值
throw new Error("error message")
console.log("end");
}
foo().catch(err => {
console.log("err:", err);
})
console.log("后续的业务代码");
async函数另外一个特殊之处就是可以在它内部使用await关键字,而普通函数中是不可以的。
await关键字的特点:
通常使用await是后面会跟上一个表达式,这个表达式会返回一个Promise;
那么await会等到Promise的状态变成fulfilled状态,之后继续执行异步函数; 后面的代码相当于await返回的promise对象调用then里面需要执行的代码
awiait后面表达式的值:
如果await后面是一个普通的值,那么会直接返回这个值;
如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值;
如果await后面的表达式,返回的Promise是reject的状态,那么会将这个reject结果直接作为函数的Promise的 reject值;
代码示例:
function requestData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('aaa')
reject(111)
},2000)
})
}
async function foo() {
// 这个表达式会返回一个Promise
const res1 = await requestData()
// 下面的代码相当于上面的await返回的promise.then里面执行的代码
console.log("后面的代码", res1);
}
foo()
// 2.其他值
async function foo() {
// 2.1 普通值
const res1 = await 'abc'
console.log("后面的代码", res1);
// 2.2 对象并且实现thenable
const res2 = await {
then: function(resolve, reject) {
resolve("aaa")
}
}
console.log(res2);
// 2.3 Promise
const res3 = await new Promise(resolve => {
resolve("zyk")
})
console.log(res3);
}
foo()
await后面的promise如果返回reject,那么就需要在async函数返回值使用catch捕获错误,然后继续执行后面的代码
// 3.reject
async function foo() {
const res1 = await requestData()
console.log("res1:", res1);
}
foo().catch(err => {
console.log(err);
})