Promise原理解析,让你彻底明白Promise原理
分类:前端技术

30分钟,让你彻底明白Promise原理

2017/05/27 · JavaScript · Promise

原文出处: 前端静径   

### 背景

=

Promise原理解析

标签(空格分隔): Node.js


前言

前一阵子记录了promise的一些常规用法,这篇文章再深入一个层次,来分析分析promise的这种规则机制是如何实现的。ps:本文适合已经对promise的用法有所了解的人阅读,如果对其用法还不是太了解,可以移步我的上一篇博文。

本文的promise源码是按照Promise/A 规范来编写的(不想看英文版的移步Promise/A 规范中文翻译)

Promise是异步编程的一种解决方案,它可以解决异步回调地狱的问题,防止层层嵌套对程序代码带来的难维护性。既然带来了方便,我们就有必要学习它的原理以及底层实现,所以笔者就按照PromiseA 规范写了一个简单的Promise,并实现了Promise.all(),Promise.race()等API

前言

本文旨在简单讲解一下javascript中的Promise对象的概念,特性与简单的使用方法。并在文末会附上一份符合PromiseA 规范的Promise对象的完整实现。

注:本文中的相关概念均基于PromiseA 规范。

相关参考

JavaScript Promise迷你书

Promise/A 规范


Promise原理解析

引子

为了让大家更容易理解,我们从一个场景开始讲解,让大家一步一步跟着思路思考,相信你一定会更容易看懂。

考虑下面一种获取用户id的请求处理

//例1 function getUserId() { return new Promise(function(resolve) { //异步请求 http.get(url, function(results) { resolve(results.id) }) }) } getUserId().then(function(id) { //一些处理 })

1
2
3
4
5
6
7
8
9
10
11
12
13
//例1
function getUserId() {
    return new Promise(function(resolve) {
        //异步请求
        http.get(url, function(results) {
            resolve(results.id)
        })
    })
}
 
getUserId().then(function(id) {
    //一些处理
})

getUserId方法返回一个promise,可以通过它的then方法注册(注意注册这个词)在promise异步操作成功时执行的回调。这种执行方式,使得异步调用变得十分顺手。

### 实现过程

正文

简介

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

Promise中有几个状态:

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

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

图片 1

原理剖析

那么类似这种功能的Promise怎么实现呢?其实按照上面一句话,实现一个最基础的雏形还是很easy的。

1.定义Promise,并传入一个需要执行的task函数,以及Promise中非常重要的三种状态

1.Promise简介

在了解javescript中的Promise实现之前有必要先了解一下Promise的概念。

详细原理剖析

极简promise雏形

function Promise(fn) { var value = null, callbacks = []; //callbacks为数组,因为可能同时有很多个回调 this.then = function (onFulfilled) { callbacks.push(onFulfilled); }; function resolve(value) { callbacks.forEach(function (callback) { callback(value); }); } fn(resolve); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Promise(fn) {
    var value = null,
        callbacks = [];  //callbacks为数组,因为可能同时有很多个回调
 
    this.then = function (onFulfilled) {
        callbacks.push(onFulfilled);
    };
 
    function resolve(value) {
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }
 
    fn(resolve);
}

上述代码很简单,大致的逻辑是这样的:

  1. 调用then方法,将想要在Promise异步操作成功时执行的回调放入callbacks队列,其实也就是注册回调函数,可以向观察者模式方向思考;
  2. 创建Promise实例时传入的函数会被赋予一个函数类型的参数,即resolve,它接收一个参数value,代表异步操作返回的结果,当一步操作执行成功后,用户会调用resolve方法,这时候其实真正执行的操作是将callbacks队列中的回调一一执行;

可以结合例1中的代码来看,首先new Promise时,传给promise的函数发送异步请求,接着调用promise对象的then属性,注册请求成功的回调函数,然后当异步请求发送成功时,调用resolve(results.id)方法, 该方法执行then方法注册的回调数组。

相信仔细的人应该可以看出来,then方法应该能够链式调用,但是上面的最基础简单的版本显然无法支持链式调用。想让then方法支持链式调用,其实也是很简单的:

this.then = function (onFulfilled) { callbacks.push(onFulfilled); return this; };

1
2
3
4
this.then = function (onFulfilled) {
    callbacks.push(onFulfilled);
    return this;
};

see?只要简单一句话就可以实现类似下面的链式调用:

// 例2 getUserId().then(function (id) { // 一些处理 }).then(function (id) { // 一些处理 });

1
2
3
4
5
6
// 例2
getUserId().then(function (id) {
    // 一些处理
}).then(function (id) {
    // 一些处理
});

```

什么是Promise?

关于Promise概念的解释,网上的各种资料众说纷纭,这里奉上笔者自己的理解。简单来说,Promise就是一套处理异步事件的方式和流程。promise在英文中的含义是约定,而针对异步事件特性的处理方式与这个含义非常吻合。

前言

前一阵子记录了promise的一些常规用法,这篇文章再深入一个层次,来分析分析promise的这种规则机制是如何实现的。ps:本文适合已经对promise的用法有所了解的人阅读,如果对其用法还不是太了解,可以移步我的上一篇博文。
本文的promise源码是按照Promise/A 规范来编写的(不想看英文版的移步Promise/A 规范中文翻译)

加入延时机制

细心的同学应该发现,上述代码可能还存在一个问题:如果在then方法注册回调之前,resolve函数就执行了,怎么办?比如promise内部的函数是同步函数:

// 例3 function getUserId() { return new Promise(function (resolve) { resolve(9876); }); } getUserId().then(function (id) { // 一些处理 });

1
2
3
4
5
6
7
8
9
// 例3
function getUserId() {
    return new Promise(function (resolve) {
        resolve(9876);
    });
}
getUserId().then(function (id) {
    // 一些处理
});

这显然是不允许的,Promises/A 规范明确要求回调需要通过异步方式执行,用以保证一致可靠的执行顺序。因此我们要加入一些处理,保证在resolve执行之前,then方法已经注册完所有的回调。我们可以这样改造下resolve函数:

function resolve(value) { setTimeout(function() { callbacks.forEach(function (callback) { callback(value); }); }, 0) }

1
2
3
4
5
6
7
function resolve(value) {
    setTimeout(function() {
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }, 0)
}

上述代码的思路也很简单,就是通过setTimeout机制,将resolve中执行回调的逻辑放置到JS任务队列末尾,以保证在resolve执行时,then方法的回调函数已经注册完成.

但是,这样好像还存在一个问题,可以细想一下:如果Promise异步操作已经成功,这时,在异步操作成功之前注册的回调都会执行,但是在Promise异步操作成功这之后调用的then注册的回调就再也不会执行了,这显然不是我们想要的。

//定义Promise的三种状态

为什么要使用Promise?

一个异步事件不会立刻返回结果,这时我们就需要预先规定一些操作,等待异步事件返回结果后,再去使用某种方式让预先规定的操作执行。在javascript的习惯中,我们常用回调函数(callback)去实现上述过程。下面是一个简单的示例:

例1

let asyncFunc = function(callback){

    let num = 100;

    setTimeout(function(){

        num = 100;

        callback(num);

    },2000);

};

function foo(value){

    console.log(value);  //value => 200

}

asyncFunc (foo);

上面就是一个简单的异步操作处理过程,asyncFunc就是一个异步的函数,执行后通过setTimeout方法在2秒返回了一个值,而foo则是一个回调函数,通过传入异步函数并且在返回结果后被调用的方式获取异步操作的结果。这里的回调函数就如同一个事先的约定,在异步操作返回结果后立即被实现。

那么,既然js中已经有处理异步事件的方法,为何还要引入Promise这个新的方式呢?实际上,上面这段代码只是简单展示下回调函数的基础使用,而在真正的使用场景中,我们不得不面对各种十分复杂的局面。通常在一个异步操作返回结果后执行的回调中还要进行另一个异步操作,而同一个异步操作返回结果后要执行的回调函数可不止一个。数个异步操作与回调函数彼此嵌套,时刻挑战者维护和使用者的神经。下面是一个彼此嵌套的例子:

例2

ajax(url1,function(value1){

    foo(value1);

    bar();

});

function foo(value){

    ajax(url2,function(value2){

        do something..

        ajax(url3,function(value3){

            ...

        })

    });

}

function bar(){ do something.. };

上面的例子模拟了一个js中一个常用的异步操作:发送ajax请求数据。在url1请求的回调中使用了foo和bar两个函数,而foo中又发送了url2,url3的请求。。。这样数层嵌套下来,最终导致代码非常的不直观,维护起来难度也直线上升,形成常说的“回调地狱”。

了解了传统上js处理异步操作的复杂和困难后,我们不禁思索,是否有方法能够更加简洁,直观的去解决异步操作的种种问题?答案就是我们这篇文章的主角:Promise。

引子

为了让大家更容易理解,我们从一个场景开始讲解,让大家一步一步跟着思路思考,相信你一定会更容易看懂。
考虑下面一种获取用户id的请求处理

//例1
function getUserId() {
    return new Promise(function(resolve) {
        //异步请求
        http.get(url, function(results) {
            resolve(results.id)
        })
    })
}

getUserId().then(function(id) {
    //一些处理
})

getUserId方法返回一个promise,可以通过它的then方法注册(注意注册这个词)在promise异步操作成功时执行的回调。这种执行方式,使得异步调用变得十分顺手。

加入状态

恩,为了解决上一节抛出的问题,我们必须加入状态机制,也就是大家熟知的pendingfulfilledrejected

Promises/A 规范中的2.1Promise States中明确规定了,pending可以转化为fulfilledrejected并且只能转化一次,也就是说如果pending转化到fulfilled状态,那么就不能再转化到rejected。并且fulfilledrejected状态只能由pending转化而来,两者之间不能互相转换。一图胜千言:

图片 2

改进后的代码是这样的:

function Promise(fn) { var state = 'pending', value = null, callbacks = []; this.then = function (onFulfilled) { if (state === 'pending') { callbacks.push(onFulfilled); return this; } onFulfilled(value); return this; }; function resolve(newValue) { value = newValue; state = 'fulfilled'; setTimeout(function () { callbacks.forEach(function (callback) { callback(value); }); }, 0); } fn(resolve); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function Promise(fn) {
    var state = 'pending',
        value = null,
        callbacks = [];
 
    this.then = function (onFulfilled) {
        if (state === 'pending') {
            callbacks.push(onFulfilled);
            return this;
        }
        onFulfilled(value);
        return this;
    };
 
    function resolve(newValue) {
        value = newValue;
        state = 'fulfilled';
        setTimeout(function () {
            callbacks.forEach(function (callback) {
                callback(value);
            });
        }, 0);
    }
 
    fn(resolve);
}

上述代码的思路是这样的:resolve执行时,会将状态设置为fulfilled,在此之后调用then添加的新回调,都会立即执行。

这里没有任何地方将state设为rejected,为了让大家聚焦在核心代码上,这个问题后面会有一小节专门加入。

const PENDING =  'pending';

2. Promise的特性及使用

在PromiseA 规范中做出了这样定义:promise是一个包含了兼容Promise规范then方法的对象或函数,与Promise最主要的交互方法是通过将函数传入它的then方法从而获取得Promise最终的值或Promise最终最拒绝(reject)的原因。

  这段定义有两个重点:1.Promise是一个对象或函数  2.它有一个then方法,能够获取prmose的最终结果。下面我们就来实际看一下Promise到底是如何处理异步事件的,我们将上面的例1使用Promise进行一下改写:

例3

let p = new Promise(function(resolve,reject){

    let value = 100;

    setTimeout(function(){

        value = 100;

        resolve(value);

    },2000);

});

p.then(function(value){

    console.log(value);      //value => 200

},function(err){

    do something...

});

初看之下其实并没有太大区别,但实际上Promise的威力在更复杂的场景下才能更好的发挥。我们先针对这个简单的例子来讲解下Promise的使用

首先通过 new 关键字实例化一个Promise对象,在这个对象中传入一个要执行异步操作的函数。这个函数包含两个形参:resolve和reject。这两个形参是Promise中定义的2个函数,分别在异步事件成功和失败时调用。例3中我们在2秒后调用了resolve函数,代表着异步事件成功,返回一个值。而在我们实例化Promise对象的同时,我们又调用了这个实例的then方法。then方法可以说是Promise方法中的核心,它即代表着Promise约定的这层含义,在then方法中接收2个函数作为参数,分别在异步事件成功时或失败时执行,并且两个函数的参数正是异步事件成功时返回的值或失败时原因。

其实,使用Promise对象来处理异步事件比起使用传统的回调函数的一个优点在于:Promise规范了处理异步事件的流程。我们不必再深入异步事件的内部,去分析种种状态变化后对应的回调究竟如何调用,也不必过多考虑异步事件内部发生错误时该如何捕获,我们只需要在合适的时候通知Promise返回成功或失败状态,剩下的事统统交给Promise去解决。

以上我们大致了解了Promise的处理流程,在详细讲解Promise对象中的方法之前有必要先了解一下Promise的状态概念。

一个Promise对象在实例化后可能拥有以下3种状态的其中之一:

Fulfilled - 当传入的异步事件成功返回值时的状态

Rejected - 当传入的异步事件失败或产生异常时的状态

Pending -  当传入的异步事件还没有结果返回时的状态

注意,任何时候Promise对象都只能处于以上其中状态的一种,当Promise对象处于Pending状态时,它可以转化成Fulfilled 或Rejected 状态,而当Promise对象处于Fulfilled 或Rejected状态时,它不能再转化成其他状态。

可以用一张图来直白的表示上面这段话

图片 3

                                                     (图片取自Promise迷你书)

在了解了Promise的三种状态后 ,接下来可以详细了解下Promise对象的几个方法

原理剖析

那么类似这种功能的Promise怎么实现呢?其实按照上面一句话,实现一个最基础的雏形还是很easy的。

链式Promise

那么这里问题又来了,如果用户再then函数里面注册的仍然是一个Promise,该如何解决?比如下面的例4

// 例4 getUserId() .then(getUserJobById) .then(function (job) { // 对job的处理 }); function getUserJobById(id) { return new Promise(function (resolve) { http.get(baseUrl id, function(job) { resolve(job); }); }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 例4
getUserId()
    .then(getUserJobById)
    .then(function (job) {
        // 对job的处理
    });
 
function getUserJobById(id) {
    return new Promise(function (resolve) {
        http.get(baseUrl id, function(job) {
            resolve(job);
        });
    });
}

这种场景相信用过promise的人都知道会有很多,那么类似这种就是所谓的链式Promise

链式Promise是指在当前promise达到fulfilled状态后,即开始进行下一个promise(后邻promise)。那么我们如何衔接当前promise和后邻promise呢?(这是这里的难点)。

其实也不是辣么难,只要在then方法里面return一个promise就好啦。Promises/A 规范中的2.2.7就是这么说哒(微笑脸)~

下面来看看这段暗藏玄机的then方法和resolve方法改造代码:

function Promise(fn) { var state = 'pending', value = null, callbacks = []; this.then = function (onFulfilled) { return new Promise(function (resolve) { handle({ onFulfilled: onFulfilled || null, resolve: resolve }); }); }; function handle(callback) { if (state === 'pending') { callbacks.push(callback); return; } //如果then中没有传递任何东西 if(!callback.onResolved) { callback.resolve(value); return; } var ret = callback.onFulfilled(value); callback.resolve(ret); } function resolve(newValue) { if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { var then = newValue.then; if (typeof then === 'function') { then.call(newValue, resolve); return; } } state = 'fulfilled'; value = newValue; setTimeout(function () { callbacks.forEach(function (callback) { handle(callback); }); }, 0); } fn(resolve); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 
function Promise(fn) {
    var state = 'pending',
        value = null,
        callbacks = [];
 
    this.then = function (onFulfilled) {
        return new Promise(function (resolve) {
            handle({
                onFulfilled: onFulfilled || null,
                resolve: resolve
            });
        });
    };
 
    function handle(callback) {
        if (state === 'pending') {
            callbacks.push(callback);
            return;
        }
        //如果then中没有传递任何东西
        if(!callback.onResolved) {
            callback.resolve(value);
            return;
        }
 
        var ret = callback.onFulfilled(value);
        callback.resolve(ret);
    }
 
    
    function resolve(newValue) {
        if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
            var then = newValue.then;
            if (typeof then === 'function') {
                then.call(newValue, resolve);
                return;
            }
        }
        state = 'fulfilled';
        value = newValue;
        setTimeout(function () {
            callbacks.forEach(function (callback) {
                handle(callback);
            });
        }, 0);
    }
 
    fn(resolve);
}

我们结合例4的代码,分析下上面的代码逻辑,为了方便阅读,我把例4的代码贴在这里:

// 例4 getUserId() .then(getUserJobById) .then(function (job) { // 对job的处理 }); function getUserJobById(id) { return new Promise(function (resolve) { http.get(baseUrl id, function(job) { resolve(job); }); }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 例4
getUserId()
    .then(getUserJobById)
    .then(function (job) {
        // 对job的处理
    });
 
function getUserJobById(id) {
    return new Promise(function (resolve) {
        http.get(baseUrl id, function(job) {
            resolve(job);
        });
    });
}
  1. then方法中,创建并返回了新的Promise实例,这是串行Promise的基础,并且支持链式调用。
  2. handle方法是promise内部的方法。then方法传入的形参onFulfilled以及创建新Promise实例时传入的resolve均被push到当前promisecallbacks队列中,这是衔接当前promise和后邻promise的关键所在(这里一定要好好的分析下handle的作用)。
  3. getUserId生成的promise(简称getUserId promise)异步操作成功,执行其内部方法resolve,传入的参数正是异步操作的结果id
  4. 调用handle方法处理callbacks队列中的回调:getUserJobById方法,生成新的promisegetUserJobById promise
  5. 执行之前由getUserId promisethen方法生成的新promise(称为bridge promise)的resolve方法,传入参数为getUserJobById promise。这种情况下,会将该resolve方法传入getUserJobById promisethen方法中,并直接返回。
  6. getUserJobById promise异步操作成功时,执行其callbacks中的回调:getUserId bridge promise中的resolve方法
  7. 最后执行getUserId bridge promise的后邻promisecallbacks中的回调。

更直白的可以看下面的图,一图胜千言(都是根据自己的理解画出来的,如有不对欢迎指正):

图片 4

const FULFILLED =  'fulfilled';

resolve()

resolve方法是在一个Promise对象实例化时传入的任务函数的第一个参数,它的作用是让Promise进入“Fulfilled ”状态,resolve方法只接受一个参数,即异步事件的返回值value。

极简promise雏形

function Promise(fn) {
    var value = null,
        callbacks = [];  //callbacks为数组,因为可能同时有很多个回调

    this.then = function (onFulfilled) {
        callbacks.push(onFulfilled);
    };

    function resolve(value) {
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }

    fn(resolve);
}

上述代码很简单,大致的逻辑是这样的:

  1. 调用then方法,将想要在Promise异步操作成功时执行的回调放入callbacks队列,其实也就是注册回调函数,可以向观察者模式方向思考;
  2. 创建Promise实例时传入的函数会被赋予一个函数类型的参数,即resolve,它接收一个参数value,代表异步操作返回的结果,当一步操作执行成功后,用户会调用resolve方法,这时候其实真正执行的操作是将callbacks队列中的回调一一执行;

可以结合例1中的代码来看,首先new Promise时,传给promise的函数发送异步请求,接着调用promise对象的then属性,注册请求成功的回调函数,然后当异步请求发送成功时,调用resolve(results.id)方法, 该方法执行then方法注册的回调数组。

相信仔细的人应该可以看出来,then方法应该能够链式调用,但是上面的最基础简单的版本显然无法支持链式调用。想让then方法支持链式调用,其实也是很简单的:

this.then = function (onFulfilled) {
    callbacks.push(onFulfilled);
    return this;
};

see?只要简单一句话就可以实现类似下面的链式调用:

// 例2
getUserId().then(function (id) {
    // 一些处理
}).then(function (id) {
    // 一些处理
});

失败处理

在异步操作失败时,标记其状态为rejected,并执行注册的失败回调:

//例5 function getUserId() { return new Promise(function(resolve) { //异步请求 http.get(url, function(error, results) { if (error) { reject(error); } resolve(results.id) }) }) } getUserId().then(function(id) { //一些处理 }, function(error) { console.log(error) })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//例5
function getUserId() {
    return new Promise(function(resolve) {
        //异步请求
        http.get(url, function(error, results) {
            if (error) {
                reject(error);
            }
            resolve(results.id)
        })
    })
}
 
getUserId().then(function(id) {
    //一些处理
}, function(error) {
    console.log(error)
})

有了之前处理fulfilled状态的经验,支持错误处理变得很容易,只需要在注册回调、处理状态变更上都要加入新的逻辑:

function Promise(fn) { var state = 'pending', value = null, callbacks = []; this.then = function (onFulfilled, onRejected) { return new Promise(function (resolve, reject) { handle({ onFulfilled: onFulfilled || null, onRejected: onRejected || null, resolve: resolve, reject: reject }); }); }; function handle(callback) { if (state === 'pending') { callbacks.push(callback); return; } var cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected, ret; if (cb === null) { cb = state === 'fulfilled' ? callback.resolve : callback.reject; cb(value); return; } ret = cb(value); callback.resolve(ret); } function resolve(newValue) { if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) { var then = newValue.then; if (typeof then === 'function') { then.call(newValue, resolve, reject); return; } } state = 'fulfilled'; value = newValue; execute(); } function reject(reason) { state = 'rejected'; value = reason; execute(); } function execute() { setTimeout(function () { callbacks.forEach(function (callback) { handle(callback); }); }, 0); } fn(resolve, reject); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
function Promise(fn) {
    var state = 'pending',
        value = null,
        callbacks = [];
 
    this.then = function (onFulfilled, onRejected) {
        return new Promise(function (resolve, reject) {
            handle({
                onFulfilled: onFulfilled || null,
                onRejected: onRejected || null,
                resolve: resolve,
                reject: reject
            });
        });
    };
 
    function handle(callback) {
        if (state === 'pending') {
            callbacks.push(callback);
            return;
        }
 
        var cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected,
            ret;
        if (cb === null) {
            cb = state === 'fulfilled' ? callback.resolve : callback.reject;
            cb(value);
            return;
        }
        ret = cb(value);
        callback.resolve(ret);
    }
 
    function resolve(newValue) {
        if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
            var then = newValue.then;
            if (typeof then === 'function') {
                then.call(newValue, resolve, reject);
                return;
            }
        }
        state = 'fulfilled';
        value = newValue;
        execute();
    }
 
    function reject(reason) {
        state = 'rejected';
        value = reason;
        execute();
    }
 
    function execute() {
        setTimeout(function () {
            callbacks.forEach(function (callback) {
                handle(callback);
            });
        }, 0);
    }
 
    fn(resolve, reject);
}

上述代码增加了新的reject方法,供异步操作失败时调用,同时抽出了resolvereject共用的部分,形成execute方法。

错误冒泡是上述代码已经支持,且非常实用的一个特性。在handle中发现没有指定异步操作失败的回调时,会直接将bridge promise(then函数返回的promise,后同)设为rejected状态,如此达成执行后续失败回调的效果。这有利于简化串行Promise的失败处理成本,因为一组异步操作往往会对应一个实际功能,失败处理方法通常是一致的:

//例6 getUserId() .then(getUserJobById) .then(function (job) { // 处理job }, function (error) { // getUserId或者getUerJobById时出现的错误 console.log(error); });

1
2
3
4
5
6
7
8
9
//例6
getUserId()
    .then(getUserJobById)
    .then(function (job) {
        // 处理job
    }, function (error) {
        // getUserId或者getUerJobById时出现的错误
        console.log(error);
    });

const REJECTED =  'rejected';

reject()

reject方法与resolve方法正好相反,它是在一个Promise对象实例化时传入的任务函数的第二个参数,它的作用是让Promise进入“Rejected”状态,reject方法同样只接受一个参数,即异步事件失败或异常的原因reason。

加入延时机制

细心的同学应该发现,上述代码可能还存在一个问题:如果在then方法注册回调之前,resolve函数就执行了,怎么办?比如promise内部的函数是同步函数:

// 例3
function getUserId() {
    return new Promise(function (resolve) {
        resolve(9876);
    });
}
getUserId().then(function (id) {
    // 一些处理
});

这显然是不允许的,Promises/A 规范明确要求回调需要通过异步方式执行,用以保证一致可靠的执行顺序。因此我们要加入一些处理,保证在resolve执行之前,then方法已经注册完所有的回调。我们可以这样改造下resolve函数:

function resolve(value) {
    setTimeout(function() {
        callbacks.forEach(function (callback) {
            callback(value);
        });
    }, 0)
} 

上述代码的思路也很简单,就是通过setTimeout机制,将resolve中执行回调的逻辑放置到JS任务队列末尾,以保证在resolve执行时,then方法的回调函数已经注册完成.

但是,这样好像还存在一个问题,可以细想一下:如果Promise异步操作已经成功,这时,在异步操作成功之前注册的回调都会执行,但是在Promise异步操作成功这之后调用的then注册的回调就再也不会执行了,这显然不是我们想要的。

异常处理

细心的同学会想到:如果在执行成功回调、失败回调时代码出错怎么办?对于这类异常,可以使用try-catch捕获错误,并将bridge promise设为rejected状态。handle方法改造如下:

function handle(callback) { if (state === 'pending') { callbacks.push(callback); return; } var cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected, ret; if (cb === null) { cb = state === 'fulfilled' ? callback.resolve : callback.reject; cb(value); return; } try { ret = cb(value); callback.resolve(ret); } catch (e) { callback.reject(e); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function handle(callback) {
    if (state === 'pending') {
        callbacks.push(callback);
        return;
    }
 
    var cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected,
        ret;
    if (cb === null) {
        cb = state === 'fulfilled' ? callback.resolve : callback.reject;
        cb(value);
        return;
    }
    try {
        ret = cb(value);
        callback.resolve(ret);
    } catch (e) {
        callback.reject(e);
    }
}

如果在异步操作中,多次执行resolve或者reject会重复处理后续回调,可以通过内置一个标志位解决。

function Promise(executor){}

Promise.prototype.then()

then方法是Promise对象方法的重中之重,它是Promise实例的方法,用来注册Promise对象成功时执行的回调函数(onFulfilled)和失败时执行的回调函数(onRejected)。一个then方法的返回值仍然是一个Promsie对象。因此,then方法支持链式调用,也就是一个一个then方法的返回值可以继续调用then。而相链接的then方法中,在上一个then方法的onFulfilled或onRejected回调函数中通过 return value(reason)的方式,把这个结果作为下一个then中的回调函数的参数被接收。onFulfilled和onRejected函数的返回值可以是任何javascript值,甚至一个Promise对象的成功或失败时的回调函数可以返回一个新的Promise对象。这样的特性使得例2中那种复杂的异步事件嵌套的场景处理得以简化。下面是使用Promise来重写的例2:

例4

let p1 = new Promise(function(resolve,reject){

    ajax(url1,function(value1){

        resolve(value1);

    });

});

p1.then(function(value1){

    return new Promise(function(resolve,reject){

        ajax(url2,function(value2){

            do something..

            resolve(value2);

        });

    })

}).then(function(value2){

    return new Promise(function(resolve,reject){

        ajax(url3,function(value3){

            ...

        });

    })

});

p1.then(bar);

function bar(){do something...};

可以看出,使用Promise改写后的代码结构上更加清晰,它把层层嵌套的函数转化成链式的调用then方法的形式,这样可以非常清晰的看出事件间的关系和执行顺序,大大降低了日后代码使用和维护的难度。

关于then方法还有几点补充:

1. then方法中的onFulfilled和onRejected方法都是可以省略的。

2. 当一个Promise失败返回了reason,而then方法中没有定义onRejected函数时,这个reason会被链式调用的下一个then方法的onRejected方法接收。

3. 一个Promise实例可以调用多次then方法,这些then注册的onFulfilled和onRejected函数会按照注册的顺序执行。

加入状态

恩,为了解决上一节抛出的问题,我们必须加入状态机制,也就是大家熟知的pending、fulfilled、rejected。

Promises/A 规范中的2.1Promise States中明确规定了,pending可以转化为fulfilled或rejected并且只能转化一次,也就是说如果pending转化到fulfilled状态,那么就不能再转化到rejected。并且fulfilled和rejected状态只能由pending转化而来,两者之间不能互相转换。一图胜千言:

图片 5

改进后的代码是这样的:

function Promise(fn) {
    var state = 'pending',
        value = null,
        callbacks = [];

    this.then = function (onFulfilled) {
        if (state === 'pending') {
            callbacks.push(onFulfilled);
            return this;
        }
        onFulfilled(value);
        return this;
    };

    function resolve(newValue) {
        value = newValue;
        state = 'fulfilled';
        setTimeout(function () {
            callbacks.forEach(function (callback) {
                callback(value);
            });
        }, 0);
    }

    fn(resolve);
}

上述代码的思路是这样的:resolve执行时,会将状态设置为fulfilled,在此之后调用then添加的新回调,都会立即执行。

这里没有任何地方将state设为rejected,为了让大家聚焦在核心代码上,这个问题后面会有一小节专门加入。

总结

刚开始看promise源码的时候总不能很好的理解then和resolve函数的运行机理,但是如果你静下心来,反过来根据执行promise时的逻辑来推演,就不难理解了。这里一定要注意的点是:promise里面的then函数仅仅是注册了后续需要执行的代码,真正的执行是在resolve方法里面执行的,理清了这层,再来分析源码会省力的多。

现在回顾下Promise的实现过程,其主要使用了设计模式中的观察者模式:

  1. 通过Promise.prototype.then和Promise.prototype.catch方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。
  2. 被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态转变和通知观察者。

```

Promise.prototype.catch()

catch方法是一个then方法的语法糖,它只接受一个失败处理函数onRejected,实际上等同于以下代码:

new Promsie.then(null,function(){

    do something...

})

Promise.all()

all方法是Promsie对象的静态方法,使用方式是 Promise.all()。all方法接收的参数为一个包含数个Promise对象实例的数组,并返回一个新的Promise实例。当数组中所有的Promse实例都返回结果后,将所有数组中的Promise实例的成功返回值传入一个数组,并将这个数组注入到all方法返回的新实例的then方法中。下面是一个all方法的使用实例:

例5

let promiseArr = [

    new Promise(function(resolve,reject){

        setTimeout(function(){

            resolve(100)

        },1000)

    }),

    new Promise(function(resolve,reject){

        setTimeout(function(){

            resolve(200)

        },500)

    })

]

Promise.all(promiseArr).then(function(valArr){

    console.log(valArr)    // valArr  => [100,200]

},function(err){

    do something...

})

all方法值得注意的有两点:

1.数组中所有promise实例都成功后的返回值,在valArr中的顺序是按照promiseArr 中promise实例的顺序来排列的。

2.当任何一个promise失败后,all方法直接将返回的Promise对象的状态变为Rejected,并调用then方法的onRejected函数,把失败的原因传递出来。

Promise.resolve()

Promsie对象本身存在一个resolve方法,它的作用是立刻返回一个状态为Fulfilled的Promise对象实例。如果你在这个resolve方法中传入的是一个Promise实例的话,那么resolve方法会保持这个Promise实例的状态,并根据它最后返回的状态来调用resolve方法返回的Promise实例then方法的onResolve或onRejected函数。

其实这个方法最常用的场景是讲一个普通的值转换成一个Promise实例。一般来说不是很常用。

链式Promise

那么这里问题又来了,如果用户再then函数里面注册的仍然是一个Promise,该如何解决?比如下面的例4:

// 例4
getUserId()
    .then(getUserJobById)
    .then(function (job) {
        // 对job的处理
    });

function getUserJobById(id) {
    return new Promise(function (resolve) {
        http.get(baseUrl   id, function(job) {
            resolve(job);
        });
    });
}

这种场景相信用过promise的人都知道会有很多,那么类似这种就是所谓的链式Promise。

链式Promise是指在当前promise达到fulfilled状态后,即开始进行下一个promise(后邻promise)。那么我们如何衔接当前promise和后邻promise呢?(这是这里的难点)。

其实也不是辣么难,只要在then方法里面return一个promise就好啦。Promises/A 规范中的2.2.7就是这么说哒(微笑脸)~

下面来看看这段暗藏玄机的then方法和resolve方法改造代码:

function Promise(fn) {
    var state = 'pending',
        value = null,
        callbacks = [];

    this.then = function (onFulfilled) {
        return new Promise(function (resolve) {
            handle({
                onFulfilled: onFulfilled || null,
                resolve: resolve
            });
        });
    };

    function handle(callback) {
        if (state === 'pending') {
            callbacks.push(callback);
            return;
        }
        //如果then中没有传递任何东西
        if(!callback.onResolved) {
            callback.resolve(value);
            return;
        }

        var ret = callback.onFulfilled(value);
        callback.resolve(ret);
    }


    function resolve(newValue) {
        if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
            var then = newValue.then;
            if (typeof then === 'function') {
                then.call(newValue, resolve);
                return;
            }
        }
        state = 'fulfilled';
        value = newValue;
        setTimeout(function () {
            callbacks.forEach(function (callback) {
                handle(callback);
            });
        }, 0);
    }

    fn(resolve);
}

我们结合例4的代码,分析下上面的代码逻辑,为了方便阅读,我把例4的代码贴在这里:

// 例4
getUserId()
    .then(getUserJobById)
    .then(function (job) {
        // 对job的处理
    });

function getUserJobById(id) {
    return new Promise(function (resolve) {
        http.get(baseUrl   id, function(job) {
            resolve(job);
        });
    });
}
  1. then方法中,创建并返回了新的Promise实例,这是串行Promise的基础,并且支持链式调用。
  2. handle方法是promise内部的方法。then方法传入的形参onFulfilled以及创建新Promise实例时传入的resolve均被push到当前promise的callbacks队列中,这是衔接当前promise和后邻promise的关键所在(这里一定要好好的分析下handle的作用)。
  3. getUserId生成的promise(简称getUserId promise)异步操作成功,执行其内部方法resolve,传入的参数正是异步操作的结果id
  4. 调用handle方法处理callbacks队列中的回调:getUserJobById方法,生成新的promise(getUserJobById promise)
  5. 执行之前由getUserId promise的then方法生成的新promise(称为bridge promise)的resolve方法,传入参数为getUserJobById promise。这种情况下,会将该resolve方法传入getUserJobById promise的then方法中,并直接返回。
  6. 在getUserJobById promise异步操作成功时,执行其callbacks中的回调:getUserId bridge promise中的resolve方法
  7. 最后执行getUserId bridge promise的后邻promise的callbacks中的回调。

参考文献

深入理解 Promise
JavaScript Promises … In Wicked Detail

1 赞 9 收藏 评论

图片 6

2.设置默认状态,并定义成功和失败的回调函数数组(为了解决链式调用的问题)

Promise.reject()

与Promise.resolve()相反,它的作用是立刻返回一个状态为Rejected的Promise对象实例。实际上这个方法是一个语法糖,它等同于以下代码:

new Promise(function(resolve,reject){

    reject(reason);

})

以上就是一个ES6中的Promise对象中所包含的常用方法。

失败处理

在异步操作失败时,标记其状态为rejected,并执行注册的失败回调:

//例5
function getUserId() {
    return new Promise(function(resolve) {
        //异步请求
        http.get(url, function(error, results) {
            if (error) {
                reject(error);
            }
            resolve(results.id)
        })
    })
}

getUserId().then(function(id) {
    //一些处理
}, function(error) {
    console.log(error)
})

有了之前处理fulfilled状态的经验,支持错误处理变得很容易,只需要在注册回调、处理状态变更上都要加入新的逻辑:

function Promise(fn) {
    var state = 'pending',
        value = null,
        callbacks = [];

    this.then = function (onFulfilled, onRejected) {
        return new Promise(function (resolve, reject) {
            handle({
                onFulfilled: onFulfilled || null,
                onRejected: onRejected || null,
                resolve: resolve,
                reject: reject
            });
        });
    };

    function handle(callback) {
        if (state === 'pending') {
            callbacks.push(callback);
            return;
        }

        var cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected,
            ret;
        if (cb === null) {
            cb = state === 'fulfilled' ? callback.resolve : callback.reject;
            cb(value);
            return;
        }
        ret = cb(value);
        callback.resolve(ret);
    }

    function resolve(newValue) {
        if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
            var then = newValue.then;
            if (typeof then === 'function') {
                then.call(newValue, resolve, reject);
                return;
            }
        }
        state = 'fulfilled';
        value = newValue;
        execute();
    }

    function reject(reason) {
        state = 'rejected';
        value = reason;
        execute();
    }

    function execute() {
        setTimeout(function () {
            callbacks.forEach(function (callback) {
                handle(callback);
            });
        }, 0);
    }

    fn(resolve, reject);
}

上述代码增加了新的reject方法,供异步操作失败时调用,同时抽出了resolve和reject共用的部分,形成execute方法。

错误冒泡是上述代码已经支持,且非常实用的一个特性。在handle中发现没有指定异步操作失败的回调时,会直接将bridge promise(then函数返回的promise,后同)设为rejected状态,如此达成执行后续失败回调的效果。这有利于简化串行Promise的失败处理成本,因为一组异步操作往往会对应一个实际功能,失败处理方法通常是一致的:

//例6
getUserId()
    .then(getUserJobById)
    .then(function (job) {
        // 处理job
    }, function (error) {
        // getUserId或者getUerJobById时出现的错误
        console.log(error);
    });

```

3. 一个符合PromiseA 规范的Promise对象的完整实现

  想必看到这里的一些读者会不禁思考,Promise对象究竟是如何实现的呢?我个人参考了一些资料实现了一个符合PromiseA 规范的Promise对象,把源代码贴在下面,有兴趣的朋友可以参考一下,实际上代码本身并不是很多,各位看完以后可以尝试用自己的方式再实现一遍。同时附上一个测试工具,里面包含了几百个测试用例,用来测试我们自己写的Promise是否完美的符合PromiseA 规范。

Compliances tests for Promises/A

使用的方法很简单

npm i -g promises-aplus-tests

promises-aplus-tests Promise.js

安装后运行你的js文件就可以测试你的代码是否符合规范了。

下面就是我实现的Promise对象的代码

function MyPromise(task) {

    const _this = this;

    _this.status = 'pending';  //设定初始状态

    _this.value = undefined;

    _this.onFulfilledsList = [];  //onFulfilled函数序列

    _this.onRejectedsList = [];  //onRejected函数序列

    function resolve(value) {

        if (value instanceof MyPromise) {

            return value.then(resolve, reject);

        }

        //异步执行resolve或reject方法,保证代码的统一性和注册的回调函数按照正确的顺序执行

            if (_this.status === 'pending') {

                _this.status = 'fulfilled';

                _this.value = value;

                _this.onFulfilledsList.forEach(cb => cb(value))

            }

    }

    function reject(reason) {

            if (_this.status === 'pending') {

                _this.status = 'rejected';

                _this.reason = reason;

                _this.onRejectedsList.forEach(cb => cb(reason))

            }

    }

    try {

        task(resolve, reject);

    } catch (err) {

        throw new Error(err);

    }

}

function resolvePromise(promise2, x, resolve, reject) {

    if (x === promise2) {

        return reject(new TypeError('循环引用'));

    }

    //如果返回的是一个thenable对象,即一个拥有then方法的对象,那么使用它的then方法去获得它的最终返回值。目的是为了兼容其他Promise库

    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {

        let then, called;

        try {

            then = x.then;

            if (typeof then === 'function') {

                then.call(x, function (newx) {

                    if (called) return;  //防止重复调用

                    called = true;

                    resolvePromise(promise2, newx, resolve, reject);

                }, function (err) {

                    if (called) return;

                    called = true;

                    return reject(err);

                });

            } else {

                resolve(x);

            }

        } catch (err) {

            if (called) return;

            called = true;

            reject(err);

        }

    } else {

        resolve(x);

    }

}

MyPromise.prototype.then = function (onFulfilled, onRejected) {

    const _this = this;

    let promise2;

    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (data) {

        return data;

    };

    onRejected = typeof onRejected === 'function' ? onRejected : function (data) {

        throw data;

    };

    //为了支持同步代码,当then方法注册的时候如果Promise的状态已经改变,那么立即执行对应的函数

    if (_this.status === 'fulfilled') {

        promise2 = new MyPromise(function (resolve, reject) {

          setTimeout(function () {

            let x;

            try {

                x = onFulfilled(_this.value);

                resolvePromise(promise2, x, resolve, reject);

            } catch (err) {

                reject(err);

            }

          })

        })

    }

    if (_this.status === 'rejected') {

        promise2 = new MyPromise(function (resolve, reject) {

         setTimeout(function () {

            let x;

            try {

                x = onRejected(_this.reason);

                resolvePromise(promise2, x, resolve, reject);

            } catch (err) {

                reject(err);

            }

         )}

        })

    }

    if (_this.status === 'pending') {

        promise2 = new MyPromise(function (resolve, reject) {

            _this.onFulfilledsList.push(function (value) {

                setTimeout(function () {

                let x;

                try {

                    x = onFulfilled(value);

                    resolvePromise(promise2, x, resolve, reject);

                } catch (err) {

                    reject(err);

                }

                })

            });

            _this.onRejectedsList.push(function (reason) {

               setTimeout(function () {

                try {

                    let x = onRejected(reason);

                    resolvePromise(promise2, x, resolve, reject);

                } catch (err) {

                    reject(err);

                }

            })

        });

        })

    }

    return promise2;  //返回一个新的Promise实例,以便支持链式调用

};

MyPromise.prototype.catch = function (onRejected) {

    this.then(null, onRejected);

};

MyPromise.all = function (someValue) {

    let resolveValArr = [];

    let count = promiseLen = 0;

    let promise2;

    promise2 = new MyPromise(function (resolve, reject) {

        let iNow = 0;

        try {

            for (let item of someValue) {

                if (item !== null && typeof item === "object") {

                    try {

                        let then = item.then;

                        let index = iNow;

                        if (typeof then === 'function') {

                            promiseLen ;

                            then.call(item, function (value) {

                                resolveValArr[index] = value;

                                if ( count === promiseLen) {

                                    resolve(resolveValArr)

                                }

                            }, function (err) {

                                reject(err);

                            });

                        }

                    } catch (err) {

                        resolveValArr[iNow] = item;

                    }

                } else {

                    resolveValArr[iNow] = item;

                }

                iNow ;

            }

            if (iNow === 0) {

                return resolve(someValue);

            }

            if (promiseLen === 0) {

                return resolve(resolveValArr);

            }

        } catch (err) {

            reject(new TypeError('无法遍历的类型!'));

        }

    });

    return promise2;

};

MyPromise.race = function (someValue) {

    let promise2;

    promise2 = new MyPromise(function (resolve, reject) {

        let iNow = 0;

        try {

            for (let item of someValue) {

                if (item !== null && typeof item === "object") {

                    try {

                        let then = item.then;

                        then.call(item, function (value) {

                            resolve(value);

                        }, function (err) {

                            reject(err);

                        });

                    } catch (err) {

                        resolve(item);

                        break;

                    }

                } else {

                    resolve(item);

                    break;

                }

                iNow ;

            }

            if (iNow === 0) {

                return resolve(someValue);

            }

        } catch (err) {

            reject(new TypeError('无法遍历的类型!'));

        }

    });

    return promise2;

};

MyPromise.resolve = function (value) {

    let promise2;

    if (value !== null && (typeof value === 'object' || typeof value === 'function')) {

        promise2 = new MyPromise(function (resolve, reject) {

            try {

                let then = value.then;

                if (typeof value.then === 'function') {

                    then.call(value, function (data) {

                        resolve(data);

                    }, reject);

                } else {

                    resolve(value);

                }

            } catch (err) {

                reject(err);

            }

        })

    } else {

        promise2 = new MyPromise(function (resolve) {

            resolve(value);

        })

    }

    return promise2;

};

MyPromise.reject = function (reason) {

    return new MyPromise(function (resolve, reject) {

        reject(reason);

    })

};

module.exports = MyPromise;

//这是为了让代码能够测试而开放的接口,详见promises-aplus-tests中的相关描述

MyPromise.deferred = MyPromise.defer = function () {

    let deferred = {};

    deferred.promise = new MyPromise(function (resolve, reject) {

        deferred.resolve = resolve;

        deferred.reject = reject;

    });

    return deferred

};

-

异常处理

细心的同学会想到:如果在执行成功回调、失败回调时代码出错怎么办?对于这类异常,可以使用try-catch捕获错误,并将bridge promise设为rejected状态。handle方法改造如下:

function handle(callback) {
    if (state === 'pending') {
        callbacks.push(callback);
        return;
    }

    var cb = state === 'fulfilled' ? callback.onFulfilled : callback.onRejected,
        ret;
    if (cb === null) {
        cb = state === 'fulfilled' ? callback.resolve : callback.reject;
        cb(value);
        return;
    }
    try {
        ret = cb(value);
        callback.resolve(ret);
    } catch (e) {
        callback.reject(e);
    } 
}

如果在异步操作中,多次执行resolve或者reject会重复处理后续回调,可以通过内置一个标志位解决。

 //设置默认状态

尾声

本文参考了很多资料,如果你看到其他文章有类似的观点非常正常,不过笔者尽量使用了自己的理解去阐述Promise的相关知识。如果你发现本文中有哪些疏漏,欢迎发私信给我进行斧正。同时也可以在下面留言给我,我会一一查看,尽量回复。

总结

刚开始看promise源码的时候总不能很好的理解then和resolve函数的运行机理,但是如果你静下心来,反过来根据执行promise时的逻辑来推演,就不难理解了。这里一定要注意的点是:promise里面的then函数仅仅是注册了后续需要执行的代码,真正的执行是在resolve方法里面执行的,理清了这层,再来分析源码会省力的多。

现在回顾下Promise的实现过程,其主要使用了设计模式中的观察者模式:

  1. 通过Promise.prototype.then和Promise.prototype.catch方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。
  2. 被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态转变和通知观察者。

self.status = PENDING;

参考文档

  • 30分钟,让你彻底明白Promise原理
  • Promise原理浅析

//存放成功的回调函数的数组

self.onResolvedCallbacks =[];

//定义存放失败回调函数的数组

self.onRejectedCallbacks =[];

```

3.定义成功和失败的回调函数实现

```

function resolve(value){ 

if(value!=null &&value.then&&typeof value.then == 'function'){

  return value.then(resolve,reject);

}

// This can be implemented with either a“macro-task”mechanism such as setTimeout or setImmediate,or with a“micro-task”mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code

setTimeout(function(){

  if(self.status == PENDING){

    self.status = FULFILLED;

    self.value = value;

    self.onResolvedCallbacks.forEach(cb=>cb(self.value));

  }

})

}

//  When rejected,a promise:

// must not transition to any other state.

// must have a reason,which must not change.

function reject(reason){

setTimeout(function(){

  if(self.status == PENDING){

    self.status = REJECTED;

    self.value = reason;

    self.onRejectedCallbacks.forEach(cb=>cb(self.value));

  }

});

}

```

4.实现then方法,这个很重要,就是异步任务执行成功调用then方法,依次走下去,避免了回调黑洞,其中resolvePromise严格按照[PromiseA 规范](

```

Promise.prototype.then = function(onFulfilled,onRejected){

  onFulfilled = typeof onFulfilled == 'function'?onFulfilled:function(value){return  value};

  onRejected = typeof onRejected == 'function'?onRejected:reason=>{throw reason};

  let self = this;

  let promise2;

  if(self.status == FULFILLED){

    return promise2 = new Promise(function(resolve,reject){

      setTimeout(function(){

        try{

          let x =onFulfilled(self.value);

          resolvePromise(promise2,x,resolve,reject);

        }catch(e){

          reject(e);

        }

      })

    });

  }

  if(self.status == REJECTED){

    return promise2 = new Promise(function(resolve,reject){

      setTimeout(function(){

        try{

          let x =onRejected(self.value);

          resolvePromise(promise2,x,resolve,reject);

        }catch(e){

          reject(e);

        }

      })

    });

  }

  if(self.status == PENDING){

   return promise2 = new Promise(function(resolve,reject){

     self.onResolvedCallbacks.push(function(){

         try{

           let x =onFulfilled(self.value);

           //如果获取到了返回值x,会走解析promise的过程

           resolvePromise(promise2,x,resolve,reject);

         }catch(e){

           reject(e);

         }

     });

     self.onRejectedCallbacks.push(function(){

         try{

           let x =onRejected(self.value);

           resolvePromise(promise2,x,resolve,reject);

         }catch(e){

           reject(e);

         }

     });

   });

  }

}

function resolvePromise(promise2,x,resolve,reject){

  if(promise2 === x){

    return reject(new TypeError('构成循环引用'));

  }

  //promise2是否已经resolve或reject了

  let called = false;

  if(x instanceof Promise){

    if(x.status == PENDING){

      x.then(function(y){

        resolvePromise(promise2,y,resolve,reject);

      },reject);

    }else{

      x.then(resolve,reject);

    }

  //x是一个thenable对象或函数,只要有then方法的对象,

  }else if(x!= null &&((typeof x=='object')||(typeof x == 'function'))){

   try{

     let then = x.then;

     if(typeof then == 'function'){

       then.call(x,function(y){

          if(called)return;

          called = true;

          resolvePromise(promise2,y,resolve,reject)

       },function(err){

         if(called)return;

         called = true;

         reject(err);

       });

     }else{

       //x不是一个thenable对象

       resolve(x);

     }

   }catch(e){

     if(called)return;

     called = true;

     reject(e);

   }

  }else{

    resolve(x);

  }

}

```

5.Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。只有所有实例的状态都变成fulfilled,最后的状态才会变成fulfilled,此时返回值组成一个数组,传递给最终的回调函数。

```

function gen(times,cb){

  let result =[],count=0;

  return function(i,data){

    result[i]= data;

    if( count==times){

      cb(result);

    }

  }

}

Promise.all = function(promises){

 return new Promise(function(resolve,reject){

   let done = gen(promises.length,resolve);

   for(let i=0;i

     promises[i].then(function(data){

       done(i,data);

     },reject);

   }

 });

}

```

6.Promise.race方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。但是只要多个实例之中有一个实例率先改变状态,最终的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给最终的回调函数。

```

Promise.race = function(promises){

  return new Promise(function(resolve,reject){

    for(let i=0;i

      promises[i].then(resolve,reject);

    }

  });

}

```

### 参考链接

1.[PromiseA 规范]()

2.[PromiseA ]()

本文由pc28.am发布于前端技术,转载请注明出处:Promise原理解析,让你彻底明白Promise原理

上一篇:流程调节,必知必会 下一篇:没有了
猜你喜欢
热门排行
精彩图文