zoukankan      html  css  js  c++  java
  • What can I yield?

    浏览器支持情况:Enabled by default in desktop Chrome 39 

    一句话回答这个问题是:Promise,Thunks。为什么没有Generators?因为Generators被转为了Promise。

    Koa使用的是co库。所以在co中定义yield后的语句是什么,koa就默认支持,比如:Promise,Thunks,Generators,Arrays,Object,注意这里的Array和Object的每一项,每一个值都必须是Promise,Thunks,Generators的一种。同时在co里的generator里的yield后面也只能是Promise,Thunks,Generators,最后所有Promise,Thunks,Generators,Arrays,Object都封装成了Promise。

    流程图如下:

    co内部封装了onFulfilledonRejected函数,当yield右侧的promise resolve之后,则会调用onFullfield函数,onFullfield有一个关键的地方是:会调用gen.next(res)方法,用以给yield表达式赋值并执行下一次迭代。

    co到底做了什么?我们先来看一段源码。

    //这是一个thunk函数,只不过是用做倒计时
    function sleep(msg) {
        return function(done){
            setTimeout(function (){
                done("", msg)
            }, 1000);
        }
    }
    
    //Generator
    function * G(msg){
        return yield sleep(msg);
    }
    
    //Promise
    function P(msg){
        return new Promise(function (resolve, reject){
            setTimeout(function (){
                resolve(msg);
            }, 1000)
        });
    }
    
    //Thunk
    function Thunk(msg){
        return function (callback){
            setTimeout(function (){
                callback("", msg);
            }, 1000)
        }
    }
    
    //step 1
    co(function *(){
        console.log("before co");
    
        //step 2
        var generatorResult = yield G("Generator");
        console.log("after result " + generatorResult);
    
        //step 3
        var promiseResult = yield P("Promise");
        console.log("after result " + promiseResult);
        
        var thunkResult = yield Thunk("Thunk");
        console.log("after result " + thunkResult);
    
        console.log("after co");
    });
    
    //console.log 结果
    2016-05-26 11:38:57.578 before co
    2016-05-26 11:38:58.584 index.js:37 after result Generator
    2016-05-26 11:38:59.588 index.js:40 after result Promise
    2016-05-26 11:39:00.590 index.js:43 after result Thunk
    2016-05-26 11:39:00.591 index.js:45 after co
    

    后面的逻辑:

    • step 1:co函数拿到参数Generator后封装成一个Promise,并且立即执行onFulfilled方法,在onFulfilled方法里会调用co的Generator的next方法,获取到后面的表达式结果,并且传递给next方法。next方法会把传递值都转为promise
    function onFulfilled(res) {
      var ret;
      try {
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      
      next(ret);
      return null;
    }
    
    • step 2:调用next方法后会获得G("Generator")的返回值,判断执行后是一个Generator,然后再继续step 1逻辑,这时再调用onFulfilled方法后next方法获得是一个Thunk函数,并且把Thunk通过方法thunkToPromise封装为Promise,注意这个thunkToPromise方法
      //co.js
      function thunkToPromise(fn) {
        var ctx = this;
        return new Promise(function (resolve, reject) {
          fn.call(ctx, function (err, res) {
            if (err) return reject(err);
            if (arguments.length > 2) res = slice.call(arguments, 1);
            resolve(res);
          });
        });
      }
    

    这个Thunk的参数是一个function,并且返回值第一个是err,第二个真实的值,这个是node回调规范,当回调执行后,就会触发resolve方法,在resolve里已经注册了onFulfilled方法,会触发Generator的next方法,把值通过Generator的next机制传递到generatorResult变量里。这样step2就完成了。

    注意co里的next方法很有意思,注意第四行代码。

      //co.js
      function next(ret) {
        if (ret.done) return resolve(ret.value);
        var value = toPromise.call(ctx, ret.value);
        if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
        return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
      + 'but the following object was passed: "' + String(ret.value) + '"'));
      }
    
    • step 3,这一步就是step2的简化版,当promise的resolve方法执行后,会触发onFulfilled方法,继续把值通过Generator的next机制传递到promiseResult变量里

    • 同理 step 4

    知道这些就可以很好的使用co方法,来做到同步书写代码了。我们可以在yield后面传递Array,Object看完源码就一目了然。

    Array:是把数组每一项转化为Promise,然后用all方法

    ​```javascript
    //co.js
    function arrayToPromise(obj) {
    return Promise.all(obj.map(toPromise, this));
    }

    
    Object:遍历对象,把每一项推到数组里,用Promise.all方法封装
    
    ```javascript
    //co.js
    function objectToPromise(obj){
      var results = new obj.constructor();
      var keys = Object.keys(obj);
      var promises = [];
      for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        var promise = toPromise.call(this, obj[key]);
        if (promise && isPromise(promise)) defer(promise, key);
        else results[key] = obj[key];
      }
      //遍历对象,把每一项推到数组里,用Promise.all方法封装
      return Promise.all(promises).then(function () {
        return results;
      });
    
      function defer(promise, key) {
        // predefine the key in the result
        results[key] = undefined;
        promises.push(promise.then(function (res) {
          results[key] = res;
        }));
      }
    }
    
  • 相关阅读:
    java字符串常用操作(查找、截取、分割)
    java StringBuffer的length()和capacity()方法比较
    java四种权限修饰符
    HDU-Tick and Tick
    HDU
    Piggy-Bank (完全背包)
    HDU
    1008 Elevator (20 分)(模拟)
    最少拦截系统 (动态规划)
    外星人的语言(进制转换)
  • 原文地址:https://www.cnblogs.com/xiaoniuzai/p/6419128.html
Copyright © 2011-2022 走看看