大家好,相信大家在工作学习的过程中,势必会遇到异步的情况,那么今天和大家聊一聊前端异步处理方案的发展历程。
一、首先让我们看看有哪些前端异步处理方案
-
Callback
-
Promise
-
Generator
-
async/await
二、实现和优缺点
1.Callback(回调函数)
setTimeout(function(){
// 回调函数
},3000);
优点:
- 回调函数作为较早的异步处理方案,成功解决了同步的问题(因为JavaScript是单线程的语言,如果有一个耗时很长的任务,就会阻塞代码的执行,回调函数成功解决了这一问题)。
缺点:
- 回调函数地狱,当回调函数中嵌套了回调函数,当个数逐渐增多,代码的耦合性增高,改动代码引起的影响变大,代码的理解难度增加,程序会变的很难维护。
- 不能用try/catch捕获异常,同样return返回值也没有作用。
2.Promise
Promise是ES6中提出来的一种异步解决方案,调用resolve可以使Promise的状态从pending变成fulfilled(成功),可以在.then里做后续成功处理;reject可以使Promise的状态从pending变成rejected(失败),需要在.catch里做错误处理,同时Promise也会自动捕获内部的异常,并使你可以在catch里做处理。
new Promise(function(resolve,reject){
resolve('success'); // 成功回调
//reject('error'); //失败回调
//throw new Error('auto catch'); //抛出异常
})
.then(function(res){
console.log(res); // success
})
.catch(function(error){
console.log(error); // error});
优点:
- 链式调用解决了回调函数嵌套带来的回调地狱。
- 可以在.catch里做异常处理。
- 你可以在.then的回调函数里继续返回Promise,可以继续用.then做处理。
缺点:
- Promise无法取消
- 如果链式调用.then过多,你只会看到一堆的then,其中的逻辑会比较难以理解。
3.Generator
Generator是ES6中提出的另一种异步解决方案,它和函数很相似,但是需要在函数的函数名前加一个*号,同时内部可以使用yield。一般函数只会有一个return返回值,但是Generator中的每一个yield都可以是一个返回值,但是区别在于yield返回之后函数并没有结束,yield只是一个中断点,你可以使用Generator提供的next()方法做持续访问,next()方法会执行Generator中的代码,然后,每次遇到yield就返回一个对象{value:xxx,done:true/false},然后“暂停”。返回值里的done用来标记这个Generator是否已经结束啦,如果是true,就不要继续使用next()了。
function* myGenerator(){
yield '断点1';
yield '断点2';
return '结束啦';
}
const generator = myGenerator();
const generator1 = generator.next();
console.log(generator1); // {value: "断点1", done: false}
const generator2 = generator.next();
console.log(generator2); // {value: "断点2", done: false}
const generator3 = generator.next();
console.log(generator3); // {value: "结束啦", done: true}
const generator4 = generator.next();
console.log(generator4); // {value: undefined, done: true}
优点:
- 可以控制函数的执行。
- 解决了回调地狱的问题。
缺点:
目前来看好像没有什么明显的缺点。
4.async/await
async/await是目前为止异步处理的终极解决方案,await其实就是Generator和Promise的语法糖,await返回值是一个Promise,await内部基于Generator实现,是一个自执行的Generator。
async function test(){
const test1Value = await test1();
console.log(test1Value);
await test2();
await test3();
}
function test1(){
return new Promise((resolve)=>{
setTimeout(()=>{
console.log('test1 success');
resolve();
},3000);
});
}
function test2(){
setTimeout(()=>{
console.log('test2 success');
},2000);
}
function test3(){
return new Promise((resolve)=>{
setTimeout(()=>{
console.log('test3 success');
resolve();
},1000);
});
}
test();
// test1 success
// test3 success
// test2 success
可以看到上面的代码已经实现了以同步的形式去书写异步代码,但是我们会发现 test3反而在test2之前先返回了,这是因为只有在await返回的是Promise的时候才会等待成功或失败状态。
优点:
- 结构清晰,写法方便简单。
- 解决了回调地狱。
缺点:
- 因为await会阻塞后续代码的执行,如果不规范的使用await,可能会对性能造成一定的影响。
总结:
以上四种方案都解决了异步处理的问题,我们都知道JavaScript是单线程的语言,那么它又是通过什么方式实现了异步的功能的,预知后事如何,请待下回文章。