Skip to content
On this page

ES6的Promise

第三节说到 co 的4.x.x的代码通过 Promise 重构过,代码更为优雅和可读,co 是使用 Promise 的经典案例,所以借 co 来学习下 ES6 中的 Promise 对象。

(使用 koa 的好处是我们可以使用 ES6 新增的特性,所以学习 ES6 非常有必要,请看阮一峰老师的 [《ECMAScript 6 入门》](http://es6.ruanyifeng.com/#docs/generator)。)

promise 是一种用于解决回调嵌套导致的“恶魔金字塔”问题的模式,ES6 中的 Promise 对象是对 promise 规范的经典实现。

ES6 中的 Promise API 与大家常用的 jQuery、q 不大一样。

q 有个 defer() 用于定义 promise :

javascript
var deferred = Q.defer();
    FS.readFile("foo.txt", "utf-8", function (error, text) {
        if (error) {
            deferred.reject(new Error(error));
        } else {
            deferred.resolve(text);
        }
    });
    return deferred.promise;

ES6 中的 Promise API 与 rsvp 非常相似:

javascript
var promise = new Promise(function(resolve, reject){
        FS.readFile("foo.txt", "utf-8", function (error, text) {
            if (error) {
                reject(new Error(error));
            } else {
                resolve(text);
            }
        });
    }
    return promise;

使用 promise (大家都比较熟悉了就不再累述):

javascript
promise.then(function(){
        
    }).fail(function(){
    
    })

使用 Promise 优化 co 代码

我们希望 yieldable 可以是 promise(将 read 函数由 thunk 转成 promise,其他代码不变):

javascript
function read(file) {
        return new Promise(function(resolve, reject){
            fs.readFile(file, 'utf8', function(err,result){
                if (err) reject(err);
                else resolve(result);
            });
        });
    }

我们来看下 co 是如何实现的。

首先,将所有的 yieldable 都转成标准 Promise 实例(简化演示,去掉 array 、object 转 promise 过程):

javascript
//将所有的 yieldable 都转成 promise 
    function toPromise(obj) {
      if (!obj) return obj;
      if (isPromise(obj)) return obj;
      //thunk 偏函数转成 promise
      if ('function' == typeof obj) return thunkToPromise.call(this, obj);
      //如果是 generator function 或 generator 对象 调用次 co 执行此遍历器逻辑
      if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
      return obj;
    }
    
    //判断是否是 promise
    function isPromise(obj) {
      return 'function' == typeof obj.then;
    }
    
    //thunk 偏函数转成 promise
    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);
        });
      });
    }

co返回 promise ,这样可以支持 多个 co 的链式调用:

javascript
function co(gen) {
      var ctx = this;
      if (typeof gen === 'function') gen = gen.call(this);
    
      return new Promise(function(resolve, reject) {
        //执行遍历器逻辑
        onFulfilled();
        
        function onFulfilled(res) {
          var ret;
          try {
            //执行遍历器逻辑
            ret = gen.next(res);
            //ret : {value:function(){},done:false}
          } catch (e) {
            return reject(e);
          }
          next(ret);
        }
        
        function next(ret) {
          //done 为 true,执行完成
          if (ret.done) return resolve(ret.value);
          //将 yieldable 转成 Promise 实例
          var value = toPromise.call(ctx, ret.value);
          //yieldable 执行完成后 调用 onFulfilled() ,执行下一个 next()
          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) + '"'));
        }
        
        //出现 error 执行 reject()
        function onRejected(err) {
          var ret;
          try {
            ret = gen.throw(err);
          } catch (e) {
            return reject(e);
          }
          next(ret);
        }
        
      })
    }

参考