最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 异步的前世今生

    正文概述 掘金(best爱学习)   2020-12-13   424

    开始

    同步和异步的概念我想大家都了如指掌了,由于异步在我们编程中的江湖地位不可撼动, 那我们今天我们就来扒一扒那些异步的历史. 先说下异步的历史流程是这样: callback hell -> promise -> generator -> generator + thunk -> async/await

    callback hell

    话不多说, 先上代码

    fs.readFile(fileA, function (err, data) {
      fs.readFile(fileB, function (err, data) {
        fs.readFile(fileC, function (err, data) {
          // ...
        });
      });
    });
    

    不难想象,如果依次读取多个文件,就会出现多重嵌套。代码不是纵向发展,而是横向发展,如果要修改中间的某一个操作,势必会影响他的“前后邻居”,很快就会乱成一团,无法管理. 时势造英雄, 这个时候promise上场了.

    promise

    它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。采用 Promise,连续读取多个文件,写法如下。

    var readFile = require('fs-readfile-promise');
    
    readFile(fileA)
    .then(function (data) {
      console.log(data.toString());
    })
    .then(function () {
      return readFile(fileB);
    })
    .then(function (data) {
      console.log(data.toString());
    })
    .catch(function (err) {
      console.log(err);
    });
    

    任务被promise包了一下, 用 then 去加载回调函数, then返回一个promise对象, 可以看到回调函数都以链式的方法呈现, 使得维护变得轻松起来, 说白了,Promise 只不过是一种更良好的编程风格
    缺点: 一堆then, catch让我们眼花缭乱, 而操作本身的语义很难看出来

    generator函数

    这里先要扫盲下“协程”的概念, 顾名思义, 一个任务,由多个线程互相协作, 共同完成

    举个栗子

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

    yiled会使程序暂停, next会启动执行.
    调用 Generator 函数,会返回一个内部指针(即遍历器)g, 这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针g的next方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的yield语句,上例是执行到x + 2为止。

    next方法的作用是分阶段执行Generator函数。每次调用next方法,会返回一个对象,表示当前阶段的信息(value属性和done属性)。value属性是yield语句后面表达式的值,表示当前阶段的值;done属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。

    gennerator函数异步任务的实际应用

    var fetch = require('node-fetch');
    
    function* gen(){
      var url = 'https://api.github.com/users/github';
      var result = yield fetch(url);
      console.log(result.bio);
    }
    var g = gen();
    var result = g.next();
    
    result.value.then(function(data){
      return data.json();
    }).then(function(data){
      g.next(data);
    });
    

    缺点:

    1. 可以看到,虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。
    2. 需要手动调用迭代器的next方法才能继续执行

    generator的自动流程管理

    一款基于thunk函数的自动执行器,

    这里说一下我理解的thunk函数, 本来js中的thunk函数就是把多参数函数封装成单参数函数, 在回调函数里面应用剩下的参数, 而这里的辅助generator的thunk函数, 他的回调函数的作用就是

    继续执行generator函数, 由于yield命令会将程序的执行权移出 Generator 函数. 比如下面例子中的next方法就是thunk函数的回调函数

    function run(fn) {
      var gen = fn();
    
      function next(err, data) {
        var result = gen.next(data);
        if (result.done) return;
        result.value(next);
      }
    
      next();
    }
    
    function* g() {
      // ...
    }
    
    run(g);
    

    有了这个执行器,执行 Generator 函数方便多了

    举一个实际的异步请求的例子, 自动执行器的实际应用. 后来的一些成熟的co 模块, redux - saga 应该也是这种精华思想去实现自己的异步执行

    异步的前世今生

    async函数 (推荐使用)

    es7新出的, 他是对 Generator 函数的改进,async函数自带执行器。 他的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

    async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

    async函数的实现原理就是将genrator函数和自动执行器,包装在一个函数里

    promise, generator, async函数的比较

    借鉴阮一峰的一个动画例子来具体说明三者

    假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。

    Promise 的写法
    function chainAnimationsPromise(elem, animations) {
    
      // 变量ret用来保存上一个动画的返回值
      let ret = null;
    
      // 新建一个空的Promise
      let p = Promise.resolve();
    
      // 使用then方法,添加所有动画
      for(let anim of animations) {
        p = p.then(function(val) {
          ret = val;
          return anim(elem);
        });
      }
    
      // 返回一个部署了错误捕捉机制的Promise
      return p.catch(function(e) {
        /* 忽略错误,继续执行 */
      }).then(function() {
        return ret;
      });
    
    }
    

    虽然 Promise 的写法比回调函数的写法大大改进,但是一眼看上去,代码完全都是 Promise 的 API(then、catch等等),操作本身的语义反而不容易看出来。

    Generator 函数的写法
    function chainAnimationsGenerator(elem, animations) {
    
      return spawn(function*() {
        let ret = null;
        try {
          for(let anim of animations) {
            ret = yield anim(elem);
          }
        } catch(e) {
          /* 忽略错误,继续执行 */
        }
        return ret;
      });
    
    }
    

    上面代码使用 Generator 函数遍历了每个动画,语义比 Promise 写法更清晰,用户定义的操作全部都出现在spawn函数的内部。这个写法的问题在于,必须有一个任务运行器,自动执行 Generator 函数,上面代码的spawn函数就是自动执行器,它返回一个 Promise 对象,而且必须保证yield语句后面的表达式,必须返回一个 Promise

    async 函数的写法
    async function chainAnimationsAsync(elem, animations) {
      let ret = null;
      try {
        for(let anim of animations) {
          ret = await anim(elem);
        }
      } catch(e) {
        /* 忽略错误,继续执行 */
      }
      return ret;
    }
    

    可以看到 Async 函数的实现最简洁,最符合语义,几乎没有语义不相关的代码。它将 Generator 写法中的自动执行器,改在语言层面提供,不暴露给用户,因此代码量最少。如果使用 Generator 写法,自动执行器需要用户自己提供.

    异步编程发展的目标就是让异步逻辑的代码看起来像同步一样;发展到Async/await;我认为是处理异步编程探索的一个里程碑 ~


    起源地下载网 » 异步的前世今生

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元