Promise
實現一個符合 Promise/A+ 規範的 MyPromise,並實現 resolve、reject、all、race、defer、deferred等靜態方法。
MyPromise
- 作用:建立
MyPromise
例項。Promise - MyPromise接收一個回掉函式
executor
- MyPromise狀態
- pending
- 可以轉換成 fulfilled 或 rejected
- fulfilled
- 不可改變成其他狀態
- rejected
- 不可改變成其他狀態
- pending
onFulfilledCallbacks
和onRejectedCallbacks
- 兩個陣列,陣列每一項是一個函式。分別接收
then
裡面的第一個引數和第二個引數。 - 狀態是
pending
的回掉函式。
- 兩個陣列,陣列每一項是一個函式。分別接收
- resolve
promise
的狀態是fulfilled
異常是的處理函式- 接收
value
引數- 如果是
promise
,執行then
。 - 如果不是
promise
,把value
做為引數傳給onFulfilledCallbacks
裡的每個函式。
- 如果是
- reject
promise
的狀態是rejected
異常是的處理函式- 接收
reason
引數,把reason
做為引數傳給onRejectedCallbacks
裡的每個函式。
- 執行
executor
,如果有異常,拋給reject
- 因為
Promise
是在同步程式碼執行完成後再執行,所以要把Mypromise
的執行方法resolve
和reject
放在非同步佇列裡
function MyPromise(executor) {
if (typeof executor !== 'function') {
throw new TypeError('Promise resolver ' + executor + ' is not a function');
}
let self = this;
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
function resolve(value) {
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
if (self.status === 'pending') {
self.value = value;
self.status = 'fulfilled';
self.onFulfilledCallbacks.forEach(item => item(value));
}
}
function reject(reason) {
if (self.status === 'pending') {
self.reason = reason;
self.status = 'rejected';
self.onRejectedCallbacks.forEach(item => item(reason));
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
複製程式碼
MyPromise.prototype.then
- 作用:接收兩個函式引數,第一個函式的引數是
resolve
傳入的引數,第二個引數是reject
傳入的引數。Promise#then onFulfilled
MyPromise
成功時執行的方法resolve
的引數會作為value
傳給onFulfilled
onRejected
MyPromise
失敗時執行的方法reject
的引數會作為value
傳給onRejected
- 返回一個
MyPromise
例項newPromise
,方便鏈式呼叫 - 對三種狀態分別處理
- 每個狀態中建立
newPromise
fulfilled
- 直接執行
onFulfilled
,返回值x
- 把
newPromise
、x
以及newPromise
裡的resolve
、reject
做為引數傳給resolutionPromise
- 把 MyPromise 的引數放在非同步佇列裡
- 直接執行
rejected
- 直接執行
onRejected
,返回值x
- 把
newPromise
、x
以及newPromise
裡的resolve
、reject
做為引數傳給resolutionPromise
- 把 MyPromise 的引數放在非同步佇列裡
- 直接執行
pending
- 狀態待定,把
fulfilled
和rejected
裡的非同步函式分別加到onFulfilledCallbacks
和onRejectedCallbacks
的最後一位
- 狀態待定,把
- 每個狀態中建立
resolutionPromise
後面細說- 用
catch
捕獲異常,執行reject
MyPromise.prototype.then = function (onFulfilled, onRejected) {
let self = this;
typeof onFulfilled !== 'function' && (onFulfilled = function (value) {
return value;
});
typeof onRejected !== 'function' && (onRejected = function (reason) {
throw reason;
});
let newPromise;
/**
* 分別處理例項的三種狀態
*/
if (self.status === 'fulfilled') {
newPromise = new MyPromise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolutionPromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
if (self.status === 'rejected') {
newPromise = new MyPromise(function (resolve, reject) {
setTimeout(function () {
try {
let x = onRejected(self.reason);
resolutionPromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
if (self.status === 'pending') {
newPromise = new MyPromise(function (resolve, reject) {
self.onFulfilledCallbacks.push(function (value) {
setTimeout(function () {
try {
let x = onFulfilled(value);
resolutionPromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
self.onRejectedCallbacks.push(function (reason) {
setTimeout(function () {
try {
let x = onRejected(reason);
resolutionPromise(newPromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
});
}
return newPromise;
};
複製程式碼
MyPromise.prototype.catch
- 作用:捕獲異常
- 返回
MyPromise
MyPromise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
複製程式碼
The Promise Resolution Procedure
- Promise解析過程,是以一個
promise
、一個值x
及resolve
,reject
做為引數的抽象過程 promise
等於x
,reject
丟擲異常new TypeError('迴圈引用')
x
如果不是物件(不包括null
)或者函式,執行resolve(x)
- 獲取
x.then
賦值給then
then
如果是function
- 把
x
做為this
呼叫then
,第一個引數是resolvePromise
,第二個引數是rejectPromise
resolvePromise
和rejectPromise
只有第一次呼叫有效resolvePromise
引數為y
,執行resolutionPromise(promise, y, resolve, reject)
rejectPromise
引數為r
,執行reject(r)
- 把
then
如果不是function
- 執行
resolve(x)
- 執行
- 用捕獲上一步的異常
- 執行
reject(e)
- 如果執行過
resolvePromise
或rejectPromise
,忽略
- 執行
function resolutionPromise(promise, x, resolve, reject) {
if (promise === x) {
reject(new TypeError('迴圈引用'));
}
let then, called;
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
then = x.then;
if (typeof then === 'function') {
then.call(x, function (y) {
if (called)
return;
called = true;
resolutionPromise(promise, y, resolve, reject);
}, function (r) {
if (called)
return;
called = true;
reject(r);
})
} else {
resolve(x);
}
} catch (e) {
if (called)
return;
reject(e);
}
} else {
resolve(x);
}
}
複製程式碼
MyPromise 靜態方法
MyPromise.all
- 作用:Promise.all
MyPromise.all = function (promises) {
let called = false;
return new MyPromise(function (resolve, reject) {
let newArr = [], count = 0;
for (let i = 0; i < promises.length; i++) {
let item = promises[i];
if (!(item instanceof MyPromise)) {
item = MyPromise.resolve(item);
}
item.then(function (data) {
if (!called) {
newArr[i] = data;
if (i == count) {
resolve(newArr);
count++;
}
}
}, function (e) {
if (!called) {
reject(e);
called = true;
}
});
}
});
};
複製程式碼
MyPromise.race
- 作用:Promise.race
MyPromise.race = function (promises) {
return new MyPromise(function (resolve, reject) {
let called = false;
for (let i = 0; i < promises.length; i++) {
let item = promises[i];
if (!(item instanceof MyPromise)) {
item = MyPromise.resolve(item);
}
item.then(function (data) {
if (!called) {
resolve(data);
called = true;
}
}, function (e) {
if (!called) {
reject(e);
called = true;
}
});
}
})
};
複製程式碼
MyPromise.resolve
MyPromise.resolve = function (value) {
if (value instanceof MyPromise) {
return value;
}
return new MyPromise(function (resolve, reject) {
if (typeof value !== null && typeof value === 'object' && typeof value.then === 'function') {
value.then();
} else {
resolve(value);
}
})
};
複製程式碼
MyPromise.reject
MyPromise.reject = function (e) {
return new MyPromise(function (resolve, reject) {
reject(e);
})
};
複製程式碼
test
- npm i -g promises-aplus-tests
- promises-aplus-tests Promise.js