实现一个符合Promise A+规范的Promise

Promise

模拟实现一个符合Promise A+规范的promise,仅供学习其实现原理

Promise的详细使用方法可参考阮一峰老师的文章

Promise A+规范概述

promise雏形

简单点说

  1. promise 函数的参数(executor)是一个函数,这个函数有两个参数resolvereject,这两个参数也都是函数,分别在promise成功和失败时调用。

  2. 当构建一个promise实例时,会自动调用这个函数(executor)

  3. 每个promise对象都有一个onFulfilledCallback队列和一个onRejectedCallback队列,用来分别存储成功和失败时调用的回调函数
  4. 每个promise有三种状态pendingfulfilledrejected,同一时刻只能处于其中一种状态,并且只能从pending 、状态转化成fulfilled状态,或者rejected状态,一旦状态发生转化就不能再被改变。
  5. 当调用resolve(value)函数时,promise的状态会从pending转化成fulfilled,并且将resolve参数中的value值赋值给此promisevalue变量,promise的value被赋值后,就不能再次改变了;此时还会去取出onFulfilledCallback队列中所有的回调函数,并将此promisevalue作为回调函数的参数,依次执行
  6. 当调用rejected(reason)函数时,promise的状态会从pending转化成rejected,并且将rejected(reason)函数中的reason参数赋值给此promisereason变量,这个reason被赋值后,也是不能再次改变了;此时还会去取出onRejectedCallback队列中所有的回调函数,并将此promisereason作为回调函数的参数,依次执行
  7. 当executor执行抛异常时捕获这个异常,并将异常的原因作为reject函数的参数,执行reject函数

根据这些我们先写一个第一版的promise

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
function Promise(executor) {
const self = this;
self.state = 'pending';
self.value = null;
self.reason = null;
self.onFulfilledCallback = [];
self.onRejectedCallback = [];

function resolve(value) {
if(self.state === 'pending'){
self.state = 'fulfilled';
self.value = value;
self.onFulfilledCallback.foreach(fn=>fn(self.value))
}
}

function reject(reason) {
if(self.state === 'pending'){
self.state = 'rejected';
self.reason = reason;
self.onRejectedCallback.foreach(fn=>fn(self.reason))
}
}

try{
executor(resolve, reject)
}catch(e){
reject(e)
}


}

promise.then

  1. 每个promise必须提供一个then方法,并且then方法包含两个参数onFulfilledonRejected
  2. 这两个参数如果不是函数的话就直接忽略,并且将成功或失败的值传递给下一个then注册的回调函数及下一个then的onFulfilledonRejected
  3. 当执行then函数时,如果promise的状态是pending则将then中注册的成功和失败时对应的回调函数onFulfilledonRejected分别放入onFulfilledCallback队列和一个onRejectedCallback队列中
  4. 如果promise的状态是fulfilled,就直接调用onFulfilled函数,并且将此promisevalue作为onFulfilled函数的第一个参数执行
  5. 如果promise的状态为rejected,就直接调用onRejected函数,并且将此promisereason作为onRejected函数的第一个参数执行
1
2
3
4
5
6
7
8
9
10
11
12
13
Promise.prototype.then = function (onFulfilled, onRejected) {
const self = this;
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
if(self.state === 'fulfilled'){
onFulfilled(self.value);
}else if(self.state === 'rejected'){
onRejected(self.reason);
}else if(self.state === 'pending'){
self.onFulfilledCallback.push(onFulfilled);
self.onRejectedCallback.push(onRejected);
}
}

promise 链式调用

  1. 此时的promise 是不支持链式调用的,所以我们应该在then中返回一个新的promise来支持链式调用
    为什么是新的promise,而不是直接返回this呢?
    因为promise的状态一旦发生转变,就不能再次改变了,而链式调用中的then返回的promise是可以选择resolve或者reject的,所以then必须返回一个新的promise
  2. 所有的then中注册的回调函数,都应该是异步执行,标准promise的then中注册的回调函数是属于微观任务,我们这里可以用setTimeout来模拟,但是要注意的是setTimeout属于宏观任务
  3. 所有的then中注册的异步回调函数都应该放在try{}catch中执行,当执行then 中的回调函数抛出异常时,应该捕获这个异常,并将异常对象传递给reject,并调用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
Promise.prototype.then = function (onFulfilled, onRejected) {
const self = this;
return new Promise((resolve, reject) => {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
if(self.state === 'fulfilled'){
setTimeout(()=>{
try{
onFulfilled(self.value);
}catch(e){
reject(e)
}
}, 0)
}else if(self.state === 'rejected'){
setTimeout(()=>{
try{
onRejected(self.reason);
}catch(e){
reject(e)
}
}, 0)
}else if(self.state === 'pending'){
self.onFulfilledCallback.push(()=>{
setTimeout(()=>{
try{
onFulfilled(self.value);
}catch(e){
reject(e)
}
}, 0)
}));
self.onRejectedCallback.push(()=>{
setTimeout(()=>{
try{
onRejected(self.reason);
}catch(e){
reject(e)
}
}, 0)
}));
}
})
}

嵌套promise问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let promise1 = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve(new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('666')
}, 0)
}))
}, 0)
}))
}, 0)
})

let promise2 = promise1.then((value)=>{
console.log(value) //666
})

我们知道当调用resolve(value)时,如果resolve中的参数value是一个普通值,则会将value传递给then中注册的成功时的回调函数,并调用此回调函数。但是如果value不是一个普通值,而是一个promise的话,则会执行这个promise,如果执行这个promise得到的结果仍然为一个promise,则继续递归执行,直到最终执行结果为一个普通值,并且将这个执行结果作为第一个promise的执行结果,Promise A+ 规范定义了这个解析流程

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
function resolvePromise(promise2, x, resolve, reject) {
if(promise2 === x) { //2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
return reject(new TypeError('circular reference'));
}
let called = false; // 2.3.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
if(x != null && (typeof x === 'object' || typeof x === 'function')){ // 2.3.3 if x is an object or function,
try {
let then = x.then; // 2.3.3.1 Let then be x.then
if(typeof then === 'function'){ // 2.3.3.3 If then is a function, call it with x as self, first argument resolvePromise, and second argument rejectPromise, where:
then.call(x, y=>{
if(called) return; // 2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.
called = true;
resolvePromise(promise2, y, resolve, reject) // 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
}, reason=>{
if(called) return; // 2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.
called = true;
reject(reason) // 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.
})
}else{ // 2.3.3.4 If then is not a function, fulfill promise with x.
resolve(x)
}
} catch (e) { // 2.3.3.2 If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
if(called) return; // 2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.
called = true;
reject(e) // 2.3.3.3.4.2 Otherwise, reject promise with e as the reason
}
}else{ // 2.3.4 If x is not an object or function, fulfill promise with x.
resolve(x)
}

}

Promise.prototype.then = function(onFulfilled, onRejected){
const self = this;
// 2.2.1 Both onFulfilled and onRejected are optional arguments:
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

let promise2 = new Promise(function(resolve, reject){
if(self.state === PENDING){
self.resolveCallback.push(()=>{
setTimeout(() => {
try {
let x = onFulfilled(self.value)
resolvePromise(promise2, x, resolve, reject) // 2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
} catch (e) {
reject(e) // 2.2.7.2 If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
}
}, 0)
});
self.rejectCallback.push(()=>{
setTimeout(() => {
try {
let x = onRejected(self.reason)
resolvePromise(promise2, x, resolve, reject) // 2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
} catch (e) {
reject(e) // 2.2.7.2 If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
}
}, 0);
});
}else if(self.state === FULFILLED){ // 2.2.6.1 If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then.
setTimeout(() => {
try {
let x = onFulfilled(self.value) // 2.2.2.1 it must be called after promise is fulfilled, with promise’s value as its first argument.
resolvePromise(promise2, x, resolve, reject) // 2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
} catch (e) {
reject(e) // 2.2.7.2 If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
}
}, 0);
}else if(self.state === REJECTED){ // 2.2.6.2 If/when promise is rejected, all respective onRejected callbacks must execute in the order of their originating calls to then.
setTimeout(() => {
try {
let x = onRejected(self.reason)
resolvePromise(promise2, x, resolve, reject) // 2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
} catch (e) {
reject(e) // 2.2.7.2 If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
}
}, 0);
}
})
return promise2; // 2.2.7 then must return a promise
}

整理一下,最终版本

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
function Promise(executor) {
const self = this;
self.state = 'pending';
self.value = null;
self.reason = null;
self.onFulfilledCallback = [];
self.onRejectedCallback = [];

function resolve(value) {
if(self.state === 'pending'){
self.state = 'fulfilled';
self.value = value;
self.onFulfilledCallback.forEach(fn=>fn())
}
}

function reject(reason) {
if(self.state === 'pending'){
self.state = 'rejected';
self.reason = reason;
self.onRejectedCallback.forEach(fn=>fn())
}
}

try{
executor(resolve, reject)
}catch(e){
reject(e)
}
}


function resolvePromise(promise2, x, resolve, reject) {
if(promise2 === x) { //2.3.1 If promise and x refer to the same object, reject promise with a TypeError as the reason.
return reject(new TypeError('circular reference'));
}
let called = false; // 2.3.3.3.3 If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
if(x != null && (typeof x === 'object' || typeof x === 'function')){ // 2.3.3 if x is an object or function,
try {
let then = x.then; // 2.3.3.1 Let then be x.then
if(typeof then === 'function'){ // 2.3.3.3 If then is a function, call it with x as self, first argument resolvePromise, and second argument rejectPromise, where:
then.call(x, y=>{
if(called) return; // 2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.
called = true;
resolvePromise(promise2, y, resolve, reject) // 2.3.3.3.1 If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
}, reason=>{
if(called) return; // 2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.
called = true;
reject(reason) // 2.3.3.3.2 If/when rejectPromise is called with a reason r, reject promise with r.
})
}else{ // 2.3.3.4 If then is not a function, fulfill promise with x.
resolve(x)
}
} catch (e) { // 2.3.3.2 If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
if(called) return; // 2.3.3.3.4.1 If resolvePromise or rejectPromise have been called, ignore it.
called = true;
reject(e) // 2.3.3.3.4.2 Otherwise, reject promise with e as the reason
}
}else{ // 2.3.4 If x is not an object or function, fulfill promise with x.
resolve(x)
}

}

Promise.prototype.then = function(onFulfilled, onRejected){
const self = this;
// 2.2.1 Both onFulfilled and onRejected are optional arguments:
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

let promise2 = new Promise(function(resolve, reject){
if(self.state === PENDING){
self.resolveCallback.push(()=>{
setTimeout(() => {
try {
let x = onFulfilled(self.value)
resolvePromise(promise2, x, resolve, reject) // 2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
} catch (e) {
reject(e) // 2.2.7.2 If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
}
}, 0)
});
self.rejectCallback.push(()=>{
setTimeout(() => {
try {
let x = onRejected(self.reason)
resolvePromise(promise2, x, resolve, reject) // 2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
} catch (e) {
reject(e) // 2.2.7.2 If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
}
}, 0);
});
}else if(self.state === FULFILLED){ // 2.2.6.1 If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then.
setTimeout(() => {
try {
let x = onFulfilled(self.value) // 2.2.2.1 it must be called after promise is fulfilled, with promise’s value as its first argument.
resolvePromise(promise2, x, resolve, reject) // 2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
} catch (e) {
reject(e) // 2.2.7.2 If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
}
}, 0);
}else if(self.state === REJECTED){ // 2.2.6.2 If/when promise is rejected, all respective onRejected callbacks must execute in the order of their originating calls to then.
setTimeout(() => {
try {
let x = onRejected(self.reason)
resolvePromise(promise2, x, resolve, reject) // 2.2.7.1 If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
} catch (e) {
reject(e) // 2.2.7.2 If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
}
}, 0);
}
})
return promise2; // 2.2.7 then must return a promise
}


module.exports = Promise;

测试

安装promises-aplus-tests

1
npm i -g promises-aplus-tests

添加测试代码

1
2
3
4
5
6
7
8
Promise.deferred = Promise.defer = function(){
let defer = {};
defer.promise = new Promise((resolve, reject)=>{
defer.resolve = resolve;
defer.reject = reject;
})
return defer;
}

测试命令

1
promises-aplus-tests promise.js

测试结果(872 passing)

测试结果

Promise 其他常用方法实现

Promise.resolve

1
2
3
4
5
Promise.resolve = function(value){
return new Promise((resolve, reject)=>{
resolve(value)
})
}

Promise.reject

1
2
3
4
5
Promise.reject = function(reason){
return new Promise((resolve, reject)=>{
reject(reason)
})
}

promise.catch

1
2
3
Promise.prototype.catch = function(onRejected){
return this.then(null, onRejected)
}

Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Promise.all = function(arr){
let index = 0;
let result = []
return new Promise((resolve, reject)=>{
arr.forEach((promise, i)=>{
promise.then(val=>{
result[i] = val
if(++index === arr.length) { // 由于then注册的回调函数是异步执行的,无法确定回调函数什么时候执行完成,所以必须得把判断放到回调函数中,这样才能确保所有的异步任务执行完成后在调用resolve
resolve(result)
}
}, reject);
})
})
}

Promise.race

1
2
3
4
5
6
7
Promise.race = function(arr){
return new Promise((resolve, reject)=>{
arr.forEach((promise, i)=>{
promise.then(resolve, reject);
})
})
}
© 2019 GOYTH All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero