早期的网页都是静态页面,也不存在前后端分离,随着互联网的兴起了网站应用的增加,网站需要更多的和服务器交互,这时候为了实现异步处理,出现了ajax,我们采用setTimeout来模拟服务端请求。

1
2
3
4
5
6
7
8
9
function ajax() {
setTimeout(() = >{
console.log('之前执行');
},
2000)
}

ajax();
console.log('之后执行');

运行代码结果:

1
2
3
之后执行
//-- 2s后
之前执行

这并不是我想要的结果,因为代码是自上而下执行,遇到异步请求代码片段延迟执行,而异步代码片段外继续执行,这时候我们可以通过函数做为参数改进代码。

1
2
3
4
5
6
7
8
9
10
11
function ajax(fn) {
setTimeout(() = >{
console.log('之前执行');
fn()
},
2000)
}

ajax(() = >{
console.log('之后执行');
});

运行代码结果:

1
2
3
//-- 2s后
之前执行
之后执行

这样虽然暂时是我们想要的效果,但是当应用庞大,后一个请求依赖前一个请求执行的结果时,就容易陷入回调地狱模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function ajax(fn) {
setTimeout(() = >{
console.log('之前执行');
fn()
},
2000)
}

ajax(() = >{
console.log('之后执行1');
ajax(() = >{
console.log('之后执行2');
ajax(() = >{
console.log('之后执行3');
});
});
});

运行代码结果:

1
2
3
4
5
6
7
//-- 2s后
之前执行
之后执行1
之前执行
之后执行2
之前执行
之后执行3

上面这样就陷入了回调地狱,一旦项目过大,就造成难以调试和维护的问题,为了解决回调地狱的问题,Promise出现了。

1
2
3
4
5
6
7
8
9
10
11
12
function delay(text) {
return new Promise((resolve, reject) = >{
setTimeout(() = >{
resolve(text);
},
2000)
})
}

delay('hello one').then((msg) = >{
console.log(msg);
})

运行代码结果:

1
2
//-- 2s后
hello one

Promise就相当于一个承诺,内部的函数的参数有两个,一个是成功解决resolve,另一个是拒绝reject,显著特征就是thencatch的回调,如上面案例所示,如果想要嵌套执行,就需要多次链式then回调。

1
2
3
4
5
6
7
8
9
10
11
12
delay('one').then((msg) = >{
console.log(msg);
return delay('two');
}).then((msg) = >{
console.log(msg);
return delay('three');
}).then((msg) = >{
console.log(msg);
}).
catch(error = >{
//捕获代码异常
})

代码执行结果:

1
2
3
4
//-- 2s后
one
two
three

这样的代码在很大程度上解决了回调地狱的问题,但是不断地链式回调的代码看起来并不是很优雅,因此出现了async await,它让异步代码看起来和同步代码一样优雅。

1
2
3
4
5
6
7
8
9
10
async function start() {
const txt1 = await delay('one');
console.log(txt1);
const txt2 = await delay('two');
console.log(txt2);
const txt3 = await delay('three');
console.log(txt3);
}

start();

代码执行结果:

1
2
3
4
//-- 2s后
one
two
three