moonye

2 年前
  • 3095

    浏览
  • 0

    评论
  • 0

    收藏

Promise原理浅析

本文作者:IMWeb moonye 原文出处:IMWeb社区 未经同意,禁止转载

Promise介绍

项目相关demo和代码地址

介绍

Promise 对象用于延迟(deferred) 计算和异步(asynchronous ) 计算.。一个Promise对象代表着一个还未完成,但预期将来会完成的操作。 Promise 对象是一个返回值的代理,这个返回值在promise对象创建时未必已知。它允许你为异步操作的成功或失败指定处理方法。 这使得异步方法可以像同步方法那样返回值:异步方法会返回一个包含了原返回值的 promise 对象来替代原返回值。

引自MDN

它解决什么问题

一个简单的示例 执行一个动画A,执行完之后再去执行另一个动画B

    setTimeout(function(){
        //A动画
        console.log('A');
        setTimeout(function() {
            //B动画
            console.log('B');
        },300)
    },300);

这里只有两个动画,如果有更多呢,就会看到一堆函数缩进

一种写法

浏览器实现方式 可以在支持Promise的版本上运行

var p = new Promise(function(resolve, reject){
  setTimeout(function(){
    //A动画
    console.log('A');
    resolve();
  },300);
});

p.then(function(){
  setTimeout(function() {
      //B动画
      console.log('B');
  },300);
});

另一种写法(jQuery版本)

jQuery版本的实现

var deferred = $.Deferred();
setTimeout(function(){
  //A动画
  console.log('A');
  deferred.resolve();
},300);

deferred.done(function() {
  setTimeout(function() {
    //B动画
    console.log('B');
  },300)
});

好像从代码上来看,是多了几行的样子,但是能用这种串行的方式来写,感觉一定很爽吧

Promise中的概念

Promise中有几个状态:

  • pending: 初始状态, 非 fulfilled 或 rejected.
  • fulfilled: 成功的操作.
  • rejected: 失败的操作.

这里从pending状态可以切换到fulfill状态(jQuery中是resolve状态),也可以从pengding切换到reject状态,这个状态切换不可逆,且fulfilled和reject两个状态之间是不能互相切换的。

一个简单版本的实现

/**
 * simple promise 
 * @param {[type]} fun [description]
 */
function PromiseB(fun) {

    this.succArg = undefined;
    this.failArg = undefined;
    this.succCbs = [];
    this.failCbs = [];
    this._status = this.STATUS.PENDING;

    this._execFun(fun);
}

PromiseB.prototype.STATUS = {
    PENDING: 1, //挂起状态
    RESOLVE: 2, //完成状态
    REJECT: 3 //拒绝状态
};

PromiseB.prototype._isFunction = function(f) {
    return Object.prototype.toString.call(f) === '[object Function]';
};

PromiseB.prototype._exec = function(callback, arg) {
    var newcallback;

    if (this._isFunction(callback)) {
        if (callback instanceof PromiseB) {
            callback.resolve(arg);
        } else {
            newcallback = new PromiseB(callback);
            newcallback.resolve(arg);
        }
    }
};

PromiseB.prototype._execFun = function(fun) {
    var that = this;

    if (this._isFunction(fun)) {
        fun(function() {
            that.succArg = Array.prototype.slice.apply(arguments);
            that._status = that.STATUS.RESOLVE;

            that.resolve.apply(that, arguments);
        }, function() {
            that.failArg = Array.prototype.slice.apply(arguments);
            that._status = that.STATUS.REJECT;

            that.reject.apply(that, arguments);
        });
    } else {
        this.resolve(fun);
    }

};

PromiseB.prototype.resolve = function() {
    var arg = arguments,
        ret,
        callback = this.succCbs.shift();
    if (this._status === this.STATUS.RESOLVE && callback) {
        ret = callback.apply(callback, arg);
        if (!(ret instanceof PromiseB)) {
            var _ret = ret;
            ret = new PromiseB(function(resolve) {
                setTimeout(function() {
                    resolve(_ret);
                });
            });

            ret.succCbs = this.succCbs.slice();
        }
        // this._exec(callback.apply(callback, arg), arg);
    }
};

PromiseB.prototype.reject = function() {
    var arg = arguments,
        ret,
        callback = this.failCbs.shift();
    if (this._status === this.STATUS.REJECT && callback) {
        ret = callback.apply(callback, arg);
        if (!(ret instanceof PromiseB)) {
            var _ret = ret;
            ret = new PromiseB(function(resolve) {
                setTimeout(function() {
                    resolve(_ret);
                }, 200);
            });
            ret.failCbs = this.failCbs.slice();
        }
    }
};

PromiseB.prototype.then = function(s, f) {
    this.done(s);
    this.fail(f);
    return this;
};

PromiseB.prototype.done = function(fun) {
    if (this._isFunction(fun)) {
        if (this._status === this.STATUS.RESOLVE) {
            fun.apply(fun, this.succArg);
        } else {
            this.succCbs.push(fun);
        }
    }
    return this;
};

PromiseB.prototype.fail = function(fun) {
    if (this._isFunction(fun)) {
        if (this._status === this.STATUS.REJECT) {
            fun.apply(fun, this.failArg);
        } else {
            this.failCbs.push(fun);
        }
    }
    return this;
};

PromiseB.prototype.always = function(fun) {
    this.done(fun);
    this.fail(fun);
    return this;
};

总结

  • promise会让代码变得更容易维护,像写同步代码一样写异步代码
  • 了解promise的原理,写个简单的实现版本就好了
  • promise的实现方案有很多,可以看这里

相关阅读

0条评论

    您需要 注册 一个IMWeb账号或者 才能进行评论。