ES6中Promise的原理解析

转载于原文地址:https://blog.csdn.net/u013217071/article/details/76911627

简介

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

解决了什么问题及怎么使用

// 一个简单的示例 执行一个动画A,执行完之后再去执行另一个动画B
setTimeout(function(){
    //A动画
    console.log('A');
    setTimeout(function() {
        //B动画
        console.log('B');
    },300)
},300);
// 这里只有两个动画,如果有更多呢,就会看到一堆函数缩进

不难想象,如果依次有很多个动画,就会出现多重嵌套。代码不是纵向发展,而是横向发展,很快就会乱成一团,无法管理。

因为多个异步操作形成了强耦合,只要有一个操作需要修改,它的上层回调函数和下层回调函数,可能都要跟着修改。这种情况就称为回调函数地狱“(callback hell)

Promise 对象就是为了解决这个问题而提出的。它不是新的语法功能,而是一种新的写法,允许将回调函数的嵌套,改成链式调用。 

Promise 的最大问题是代码冗余,原来的任务被 Promise 包装了一下,不管什么操作,一眼看去都是一堆then,原来的语义变得很不清楚。

(1)浏览器实现方式 可以在支持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);
});

(2)另一种写法(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原理

其实,promise就是三个状态。利用观察者模式,只需要通过特定书写方式注册对应状态的事件处理函数,然后更新状态,调用注册过的处理函数即可。 

这个特定方式就是then,done,fail,always…等方法,更新状态就是resolve、reject方法。

下面简单实现:

/**
    /**
      * [3种状态]
      * @type {String}
     */
var PENDING = "pending";
var RESOLVED = "resolved";
var REJECTED = "rejected";
/**
 * [Promise类实现]
 * 构造函数传入一个fn,有两个参数,resolve:成功回调; reject:失败回调;
 * state: 状态存储
 * doneList: 成功处理函数列表
 * failList: 失败处理函数列表
 * done: 注册成功处理函数
 * fail: 注册失败处理函数
 * then: 同时注册成功和失败处理函数
 * always: 一个处理注册到成功和失败,都会调用
 * resolve: 更新state为:RESOLVED,并且执行成功处理队列
 * reject: 更新state为:REJECTED,并且执行失败处理队列
*/
var Promise2 = (function () {
    var noop = function () { }
    function Promise(fn) {
        this.state = PENDING;
        this.doneList = [];
        this.failList = [];
        this.fn = fn;
        this.fn(this.resolve.bind(this), this.reject.bind(this)) //立即执行构造函数的第一个参数
    }
    var p = {
        done: function (cb) {

            if (typeof cb == "function")
                this.doneList.push(cb)
            return this;
        },
        fail: function (cb) {
            if (typeof cb == "function")
                this.failList.push(cb);
            return this;
        },
        then: function (success, fail) {
            this.done(success || noop).fail(fail || noop)
            return this;
        },
        always: function (cb) {
            this.done(cb).fail(cb)
            return this;
        },
        resolve: function () {
            this.state = RESOLVED;
            var lists = this.doneList;
            for (var i = 0; i < lists.length;) {
                lists[i].apply(this, arguments);
                lists.shift();
            }
            return this;
        },
        reject: function () {
            this.state = REJECTED;
            var lists = this.failList;
            for (var i = 0; i < lists.length;) {
                lists[0].apply(this, arguments);
                lists.shift();
            }
            return this;
        }
    }
    for (var k in p) {
        Promise.prototype[k] = p[k]
    }
    return Promise;
})();
//测试代码
var p1 = new Promise2(function (resolve, reject) {
    setTimeout(resolve, 2000);
    console.log('p1', new Date);
});
p1.then(function () {
    console.log("end", new Date);
}).always(function () {
    console.log('always', new Date);
});

利用观察者模式,只需要通过特定书写方式,注册对应状态的事件处理函数,然后更新状态,调用注册过的处理函数即可。

解释:当我们调用new Promise(fn)时,就会立即执行第一个参数fn。上面案例中,先将then(done,fail)对应的回调函数分别加入到doneList或者failList中,always中对应的参数同时加到doneList和failList中;

当异步代码执行完毕,就调用resolve方法,此时改变promise的状态为resolved,并执行doneList里面的回调函数。如果执行失败,则调用fail方法,此时改变promise的状态为rejected,并执行failList里面的回调函数。

猜你喜欢

转载自www.cnblogs.com/minigrasshopper/p/9141307.html