异步函数我们经常会用到,最为常见的就是ajax,利用ajax请求我们的服务端获取数据,然后执行回调函数。
一,普通的ajax
ajax(url,(res)=>{
console.log(res);
})
最普通的异步请求函数,那么当我们有两个异步请求,且请求B需要依赖请求A的时候,该怎么处理?
//ajaxA
ajax(url1, (resA) => {
ajax(url2, (resB) => {
console.log(resA);
console.log(resB);
// ajax(url3, ()=>{} ... 类似这样有多少个依赖关系就一直嵌套,形成回调地狱(函数多层嵌套)。
})
})
这样处理我们的多层异步依赖,会最终形成金字塔的形状,层层嵌套,看不清代码逻辑,难以维护且难以追踪。
二,使用promise处理异步回调,使其变成链式调用。
new Promise((resolve, reject) => {
ajax(url, (res)=>{
resolve(res)//完成状态 promise的状态只可定义一次,下一行的reject将不起作用。
reject('reject')//拒绝状态
})
})
.then(res => {
return new Promise((resolve, reject) => {
//如果第二个为异步函数,继续return 一个promise,resolve的参数即是下个回调的参数。
ajax(url, (res) => {
resolve(res);
})
})
})
.then(res => {
return '非异步';//return 的值是下一个then中的参数。
})
.then(res => {
console.log(res); // "非异步"
})
至此,我们的代码看起来清晰了不少,当然我们可以遵守尽量给函数语义命名的规则,进一步的简化代码。
// 变量声明函数
let promise1 = new Promise((resolve, reject) => {
ajax(url, (res)=>{
resolve(res)//完成状态 promise的状态只可定义一次,下一行的reject将不起作用。
reject('reject')//拒绝状态
})
})
let promise2 = new Promise((resolve, reject) => {
//如果第二个依然为异步函数,继续return 一个new promise,resolve的参数即是下个回调的参数。
ajax(url, (res)=>{
resolve(res)
})
})
promise1()
.then(promise2)
.then(res => {
return '非异步';//return 的值是下一个then中的参数。
})
.then(res => {
console.log(res); // "非异步"
})
这样代码更加的简介清晰。
三,我们可以使用async和await解决回调问题。
function takeLongTime() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(10), 1000);
});
}
function takeLongTime2(v) {
console.log(v); // 10
return new Promise(resolve => {
setTimeout(() => resolve(v + 20), 10);
});
}
async function test() {
const v = await takeLongTime(); //10
const b = await takeLongTime2(v); //30
console.log(b);
}
test();
await 我们从名字可以看出来意思是等待异步的意思,即它可以让异步回调变得像同步函数,并且会阻塞下一步,但是!这一切都是发生在async中的,而async是异步的,所以整个js并不会被阻塞。所以await要写在async中。当然单纯的await和async是并不能解决问题。可以看到还是需要promise的。
async和await内部实现了Generator ,然后结合promise,是他们的语法糖,大家可以自行了解一下。(它可以使函数内部分步执行)