Javascript异步解决方案的发展历程

1.回调函数

function f1(callback){
    setTimeout(function(){
        //f1的任务代码
        callback();
    },1000);
}
f1(f2);
ajax('XXX1', () => {
    // callback 函数体
    ajax('XXX2', () => {
        // callback 函数体
        ajax('XXX3', () => {
            // callback 函数体
        })
    })
})

优点:便于理解
缺点:回调地狱,不能捕获错误

2.事件监听

f1.on('done', f2);
function f1(){
    setTimeout(function () {
      // f1的任务代码
      f1.trigger('done');
    }, 1000);
  }

容易理解,可以绑定多个事件,每个事件可以指定多个回调函数;缺点,整个流程都要变成事件驱动型,运行流程会变得不清晰。

3.发布/订阅(观察者模式)

jQuery.subscribe("done", f2);
function f1(){
    setTimeout(function () {
      // f1的任务代码
      jQuery.publish("done");
    }, 1000);
}
jQuery.unsubscribe("done", f2);

与"事件监听"类似,但是明显优于后者。因为我们可以通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

4.Promises对象

Promise就是为了解决callback的问题而产生的
Promise实现了链式调用,也就是说每次then后返回的都是一个全新的Promise,如果我们在 then 中 return ,return 的结果会被 Promise.resolve() 包装

ajax('XXX1')
  .then(res => {
      // 操作逻辑
      return ajax('XXX2')
  }).then(res => {
      // 操作逻辑
      return ajax('XXX3')
  }).then(res => {
      // 操作逻辑
  })

解决了回调地狱的问题,无法取消 Promise ,错误需要通过回调函数来捕获

5.Generator

特点:可以控制函数的执行,Generator函数是协程在ES6中的实现,最大的特点就是可以交出函数的执行权(即暂停执行)
执行Generator函数后会返回一个遍历器对象(Iterator)。本质上Generator是一个状态机,每次的遍历Generator函数返回的都是函数内部的每个状态。

    function* gen() {    
        yield 1;    
        yield 2;    
        return 1;
      }
      var g = gen();
      gen.next() // {value:1,done:false}
      gen.next() // {value:2,done:false}
      gen.next() // {value:3,done:true}

Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行。缺点还需要编写自动执行器

6.使用async函数

async函数的实现就是将Generator函数和自动执行器包装在一个函数中

function async gen(){
    awiat 1;
    awiat 2;
}

异步编程的语法目标,就是怎样让它更像同步编程,使用async/await的方法,使得异步编程与同步编程看起来相差无几了。

7.借助流程控制库

NPM社区中出现了很多流程控制库可以供开发者直接使用,其中很流行的就是async库,该库提供了一些流程控制方法,其官方文档见http://caolan.github.io/async/docs.html
如果需要执行的任务紧密结合。下一个任务需要上一个任务的结果做输入,应该使用瀑布式
如果多个任务必须依次执行,而且之间没有数据交换,应该使用串行执行
如果多个任务之间没有任何依赖,而且执行顺序没有要求,应该使用并行执行

8.使用Web Workers

Web Worker是HTML5新标准中新添加的一个功能,Web Worker的基本原理就是在当前javascript的主线程中,使用Worker类加载一个javascript文件来开辟一个新的线程,起到互不阻塞执行的效果,并且提供主线程和新线程之间数据交换的接口:postMessage,onmessage。其数据交互过程也类似于事件发布/监听模式,异能实现异步操作

9 对比

异步读取文件

1.回调函数

fs.readFile(fileA,(err,data)=>{
    if(err) throw err;
    console.log(data);
    fs.readFile(fileB,(_err,_data)=>{
        if(_err) throw err;
        console.log(_data)
    })
}

2.使用Promise

var fs = require('fs');
var readFile = function(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, (err, data) => {
            if (err) reject(err)
            resolve(data)
        })
    })
}
readFile(fileA)
.then((data)=>{console.log(data)})
.then(()=>{return readFile(fileB)})
.then((data)=>{console.log(data)})
// ... 读取n次
.catch((err)=>{console.log(err)})

3.使用Generator

var fs = require('fs');

var readFile = (path) => {
    return new Promise((resolve, reject) => {
        fs.readFile(path, (err, data) => {
            if (err) reject(err)
            resolve(data)
        })
    })
}

var gen = function* () {
    var f1 = yield readFile(fileA);
    var f2 = yield readFile(fileB);
    console.log(f1.toString());
    console.log(f2.toString());
}
gen.next()
grn.next()

4.使用Async

var asyncReadFile = async function() {
    var f1 = await readFile(fileA);
    var f2 = await readFile(fileB);
    console.log(f1.toString())
    console.log(f2.toString())
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,752评论 0 5
  • 前言 编程语言很多的新概念都是为了更好的解决老问题而提出来的。这篇博客就是一步步分析异步编程解决方案的问题以及后续...
    李向_c52d阅读 1,081评论 0 2
  • 一、Javascript实现异步编程的过程以及原理 1、为什么要用Javascript异步编程 众所周知,Java...
    Ebony_7c03阅读 873评论 0 2
  • JavaScript语言的执行环境是‘单线程’的(也就是说,执行后续的代码之前必须完成前面的任务,也就是采用的是阻...
    kim_jin阅读 579评论 0 0
  • 我从小就热爱画画,但作品不多,没有参加过专业训练班,一直很遗憾。也喜欢设计,却没做成设计师。有很多人跟我一样,被迫...
    桥焰阅读 772评论 12 12