ES6异步编程方案实现对比

445 阅读4分钟

JS语言的执行环境是“单线程”,而这有可能导致任务卡死的问题。为了解决该问题,JS将任务的执行模式分成两种:同步和异步。本文主要介绍4种异步编程方案解决回调问题。当然“事件监听”和“观察者模式”也属于js异步编程,但不在本文介绍。

JS的4种常见异步编程解决方案

1、回调函数 (es6之前)
2、new Promise (es6引入)
3、generator/yiled函数 (es6引入)
4、async/await (es8引入)

本文默认你已了解相关语法知识,既然说实现对比,我们举个例子:

案例:执行第一个步骤,等待执行完成将结果给第二个步骤;执行第二个步骤,再等待完成将结果给第三个步骤;等待执行第三个步骤完成最后输出。

1、回调函数callback实现

function step1(value1,callback) {
    setTimeout(()=>{
        let result = value1+"-step1";
        console.log(result);
        callback(result);
    },1000)
}
function step2(value2,callback) {
    setTimeout(()=>{
        let result = value2+"-step2";
        console.log(result);
        callback(result);
    },1000)
}
function step3(value3,callback) {
    setTimeout(()=>{
        let result = value3+"-step3";
        console.log(result);
        callback(result);
    },1000)
}
step1("start",function(value1){
    step2(value1,function(value2){
        step3(value2,function(value3){
            console.log("回调函数结束:",value3);
        })
    })
})
//1s后打印start-step1,2s后打印start-step1-step2,3s后打印start-step1-step2-step3
//3s后同时打印:回调函数结束start-step1-step2-step3

结论:嵌套多个回调函数,多层回调的致命缺点就是形成“回调地狱”,不利于代码的阅读和理解。各部分高度耦合,别说看别人多层回调代码,看自己的都得绕晕。

2、new Promise (es6引入)

function step1(value1){
  return new Promise(resolve=>{ //注意promise是同步执行,then才是异步执行。
      setTimeout(()=>{
          let result =value1+"-step1";
          console.log(result);
          resolve(result);
      },1000)
  })
}
function step2(value2){
  return new Promise(resolve=>{
      setTimeout(()=>{
          let result =value2+"-step2";
          console.log(result);
          resolve(result);
      },1000)
  })
}
function step3(value3){
  return new Promise(resolve=>{
      setTimeout(()=>{
          let result =value3+"-step3";
          console.log(result);
          resolve(result);
      },1000)
  })
}
let result = step1("start")
    .then(value1=>{
        return step2(value1); //记得return
    })
    .then(value2=>{
        return step3(value2);
    })
    .then(value3=>{
        console.log("promise结束",value3);
    });
//1s后打印start-step1,2s后打印start-step1-step2,3s后打印start-step1-step2-step3
//3s后同时打印:promise结束 start-step1-step2-step3

结论:promise实现多层回调存在问题是需要处理多个then链,代码也不简洁。而且无法取消 Promise,错误需要通过回调函数捕获。

3、generator/yiled函数 (es6引入)

//省略函数step1,step2,step3同上的promise一致

function* runningTask() {
    try {
      var value1 = yield step1("start");
      var value2 = yield step2(value1);
      var value3 = yield step3(value2);
      console.log("generator/yield结束",value1,value2,value3);
    } catch (e) {
    }
}
let task=runningTask();
let mystep1=task.next();
mystep1.value.then((value1)=>{
    let mystep2=task.next(value1);
    mystep2.value.then((value2)=>{
        let mystep3=task.next(value2);
        mystep3.value.then((value3)=>{
           task.next(value3);
        })
    })
})
//1s后打印start-step1,2s后打印start-step1-step2,3s后打印start-step1-step2-step3
//3s后同时打印:generator/yield结束 start-step1 start-step1-step2 start-step1-step2-step3

结论:generator最大的特点就是可以控制函数的执行。但手动迭代generator函数很麻烦,实现逻辑也比较绕。

4、async/await (es8引入)

//省略函数step1,step2,step3同上的promise一致

async function mystep(){
    let mystep1 = await step1("start");
    let mystep2 =await step2(mystep1);
    let mystep3 =await step3(mystep2);
    console.log("async/await结束",mystep1,mystep2,mystep3);
}
mystep();
//1s后打印start-step1,2s后打印start-step1-step2,3s后打印start-step1-step2-step3
//3s后同时打印:async/await结束 start-step1 start-step1-step2 start-step1-step2-step3

结论:用同步的写法执行异步的操作。async函数实际是 Generator 函数的语法糖,是对Generator的改进。写法简洁清晰易读,优雅解决“回调地狱”和“多then链”的问题。是目前js异步编程的终极解决方案。

结论/建议

(1) 当只有一次回调时,可以使用回调函数,也可以用promise,然后用.then来获取值。
(2) 当有多次回调时,推荐使用async/await异步函数。避免回调函数的“回调地狱”和promise的多then链处理。可以说async/await是目前js异步编程的终极解决方案。

后记

感谢您的阅读,不知你是否对四种异步编程方案实现差异有一定的理解?第一次在掘金写文章,平时都是整理在自己的有道云笔记,后边希望分享更多文章与大家一起学习讨论,如果觉得本文不错请点个赞,若对本文有任何的意见或建议,欢迎在评论中一起探讨。