【JavaScript——基础篇】前端异步之旅

178 阅读4分钟

大家好,相信大家在工作学习的过程中,势必会遇到异步的情况,那么今天和大家聊一聊前端异步处理方案的发展历程。

一、首先让我们看看有哪些前端异步处理方案

  1. Callback

  2. Promise

  3. Generator

  4. async/await

二、实现和优缺点

1.Callback(回调函数)

setTimeout(function(){
    // 回调函数
},3000);

优点:

  1. 回调函数作为较早的异步处理方案,成功解决了同步的问题(因为JavaScript是单线程的语言,如果有一个耗时很长的任务,就会阻塞代码的执行,回调函数成功解决了这一问题)。

缺点:

  1. 回调函数地狱,当回调函数中嵌套了回调函数,当个数逐渐增多,代码的耦合性增高,改动代码引起的影响变大,代码的理解难度增加,程序会变的很难维护。
  2. 不能用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});

优点:

  1. 链式调用解决了回调函数嵌套带来的回调地狱。
  2. 可以在.catch里做异常处理。
  3. 你可以在.then的回调函数里继续返回Promise,可以继续用.then做处理。

缺点:

  1. Promise无法取消
  2. 如果链式调用.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}

优点:

  1. 可以控制函数的执行。
  2. 解决了回调地狱的问题。

缺点:

目前来看好像没有什么明显的缺点。

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的时候才会等待成功或失败状态。

优点:

  1. 结构清晰,写法方便简单。
  2. 解决了回调地狱。

缺点:

  1. 因为await会阻塞后续代码的执行,如果不规范的使用await,可能会对性能造成一定的影响。

总结:

    以上四种方案都解决了异步处理的问题,我们都知道JavaScript是单线程的语言,那么它又是通过什么方式实现了异步的功能的,预知后事如何,请待下回文章。