谈谈我对async/await顺序执行和并行的理解

async/await是ES7规范,解决异步问题的一种实现方式。Promise对ES6异步规范的一种实现,解决之前js回调函数造成的“地狱调用”问题,也就是回调的回调这种死亡嵌套调用函数。而async/await这个语法糖可以让我们用流程式的同步语法来完成异步操作

多个异步流问题的考虑

简单来说我能想到的目前就这几种

  1. 有多个异步任务流,但是各个任务之间有着严格的执行顺序,简单称为顺序执行任务
  2. 有多个异步任务流,各个任务之间不需要考虑执行顺序,但是需要等所有任务执行完毕后才能进行下一个流程处理,简单称为并行执行任务

    Promise API的解决思路

    顺序执行任务的解决方案

function generatorAsyncTask(time, isReject, desp) {
        return new Promise(function(resolve, reject) {
        setTimeout(funciton() {
            if(isReject) {
                reject('error')
            }else {
                resolve(time)  
            }
        }, time)
    })
}

generatorAsyncTask(1000, false, 'ascytask1').then(function(successResult){
    //然后执行任务2
    generatorAsyncTask(4000, false, 'ascytask2').then(function(successResult) {
        //然后执行任务n(n>2) 以此类推
    }, function(errorResult) {
    
    })
}, function(errorResult) {

})

这样子当然可以解决问题,但是如果任何有很多,假设有10个,那么让我们发挥我们的大脑,用意念来完成这段逻辑,然后过一个星期我们再来看,你一定会觉得很糟糕,因为大量嵌套逻辑让代码的可读性变差,就会影响对业务模型的表达,如果你不写大量注释的话是无法让一个新人或者其他开发快速理解业务逻辑的,显然违法了代码的可维护性原则。

并行执行任务的解决方案

这里所说的并行执行任务可以理解为,各个任务之间并没有明显的依赖关系。举一个常见的实际应用场景,比如我们调用获取商品详情接口之前,是要先拿到商品id的,这个时候获取商品id的接口和获取商品详情接口就是属于上面所说的顺序执行任务。那如果获取详情接口不仅仅依赖商品id,现在还依赖用户其他信息,而用户其他信息接口获取和商品id接口之间是没有明显关联关系的,所以可以并行执行,等全部信息获取完毕,才能调用获取商品详情接口,这种就是并行执行任务。

接下来要敲黑板了:Promise对并行任务执行的方案还是挺好的,提供了Promise.all,这是一个原型方法,所以可以直接用构造函数进行方法属性的访问,all方法的入参是一个promise实例的数组,返回值是一个按照入参顺序拼接各个promise的resolve返回值的数组,所以如果其中有一个promise进行了reject,那么就会直接error,进入then函数的fail callback进行处理,看过了我上面的解释,那么自然而然可以知道,只有等所有的异步任务进行了resolve,才会执行then函数的success callback.

function generatorAsyncTask(time, isReject, desp) {
    return new Promise(function(resolve, reject) {
       setTimeout(function() {
            if(isReject) {
                reject('error')
            }else {
                resolve(time)  
            }
       })
    }, time)
}

Promise.all([generatorAsyncTask(1000, false, 'asynctask1'), generatorAsyncTask(2000, false, 'asynctask2'),generatorAsyncTask(12000, false, 'asynctask2')])
.then(function (succesR) {
    console.log('--succesR---', succesR)
});

Promise.all([generatorAsyncTask(1000, false, 'asynctask1'), generatorAsyncTask(2000, false, 'asynctask2'),generatorAsyncTask(12000, true, 'asynctask2')])
.then(function (succesR) {
    
}, function (failR) {
    console.log('---failR----:', failR);
});

大家可以自己运行代码进行求证,这是一篇写于2018年的草稿,但是文章被拒因为写的格式像草稿,所以我是很不擅长书面表达的,写的不好,表达的不对的地方,欢迎大家批判,姐姐我虚心接受。

async/await语法糖的写法

function wait(n, type){
    if(type === 1){**粗体**
        wait1();
    }else{
         return new Promise(function(resolve){
            setTimeout(function(){
                console.log("lala:", n);
                resolve("done:", n);
            },n)
        })
    }
}
function wait1(n){
    setTimeout(function(){
        console.log("lala:", n);
    },n)
}


//顺序执行任务sequence one by one
async function sequence(type){
    console.time(1)
    await wait(1500, type);
    await wait(500, type);
    console.timeEnd(1);
}

//并行执行任务parallel
async function parallel(type){
    console.time(2)
    const wait1 = wait(1500, type);
    const wait2 = wait(500, type);
    await wait1;
    await wait2;
    console.timeEnd(2);
}

sequence()

// lala: 1500
// VM727:4 lala: 500
// VM727:16 1: 2007.712890625ms

parallel()

// lala: 500
// VM727:4 lala: 1500
// VM727:26 2: 1506.56201171875ms

sequence(1) //1: 0.366943359375ms
parallel(1) //2: 0.376953125ms

结论

async下的 await表达式阻塞表现在(如果是异步)异步promise的状态改变,所以
sequence()的代码会等每一个await后面的promise状态返回,才会去执行下一个await。
parallel()的代码是先生成promise这个时候是非阻塞的,根据eventLoop,此时并没有其他执行栈,会在任务队列的macro任务尾部添加一个异步事件,因为是个settimeout,所以500ms的会先执行

最后哈,我说一句这个是一篇老文章了,从草稿箱翻出来的,希望我的第一篇文章能过,随着自身的不断学习和吸收,思维肯定也是会进步的,所以希望可以过,哦耶,nice day。


lemonbigpig
0 声望1 粉丝