1. promise要解決的問題:
腦筋急轉彎:把牛關進冰箱里,要分幾步?
// 第一步,打開冰箱
function open(){
setTimeout(()=>{
console.log('打開冰箱');
return 'success';
}, 1000)
}
// 第二步,放牛進去
function settle(){
setTimeout(()=>{
console.log('放牛進去');
return 'success';
}, 3000)
}
// 第三步,關上冰箱
function close(){
setTimeout(()=>{
console.log('關上冰箱');
return 'success';
}, 1000)
}
function closeCow(){
open();
settle();
close()
}
closeCow();
//"打開冰箱"
//"關上冰箱"?
//"放牛進去"?
很顯然,這三個操作不能顛倒順序,否則任務就會失敗。但是上述邏輯并不能保證最終是按照我們想要的順序進行,顯然,難點在于第二步,花費的時間更長。為了保證第二步在第一步執行成功之后再執行,第三步在第二步執行成功之后在執行,改進:
function closeCow() {
setTimeout(() => {
console.log("打開冰箱");
setTimeout(() => {
console.log("放牛進去");
setTimeout(() => {
console.log("關閉冰箱");
}, 1000);
}, 3000);
}, 1000);
}
這樣的確解決了問題,但是看起來很別扭,邏輯不清晰,這就是經典的“回調地獄”。在過去,要想做多重的異步操作,會導致經典的回調地獄,promise的出現,就是為了解決這個問題的。
Promise對象用于表示一個異步操作的最終完成 (或失敗), 及其結果值。
let closeCow = new Promise((resolve, reject) => {
console.log('把牛放進冰箱');
resolve();
});
closeCow
.then(open()) // 打開冰箱
.then(settle()) // 放牛進去
.then(close()); // 關上冰箱
這就是promise最簡單的應用。
2. promise深入了解
2.1 Promise狀態
Pending(待處理): promise初始化的狀態,正在運行,既沒有完成也沒有失敗的狀態,此狀態可以提升為fulfilled 和 rejected狀態
Fulfilled(已完成): 如果回調函數實現Promise的
resolve
(回調函數),那么state變為fulfilled
。Rejected(已拒絕): 如果Promise調用過程中遭到拒絕或發生異常,state就會處于
rejected
狀態。-
Settled(不變的): Promise從
pending
狀態提升后,狀態要么變為fulfilled
,要么變為rejected
,沒有其他情況。Promise 的狀態一旦改變,就永久保持該狀態,不會再變了。
2.2 promise實例方法
所謂實例方法(instance method)就是必須實例化之后才能調用的方法,在JS中表現為使用new
關鍵字實例化之后才能調用的,是定義在原型上的方法,即Promise.prototype.methodName
;
-
Promise.prototype.then(onFulfilled, onRejected)
: 用來注冊當狀態變為fulfilled
或者reject
時的回調函數// onFulfilled 是用來接收promise成功的值 // onRejected 是用來接收promise失敗的原因 promise.then(onFulfilled, onRejected);
注意,
then
方法是異步執行的,onFulfilled
和onRejected
方法只會執行其中一個,因為promise狀態是單向變化的,要么fulfilled
、要么rejected
:案例一:
const promise = new Promise((resolve, reject) => { resolve('fulfilled...'); // 狀態由 pending --> fulfilled }); promise.then(res => { console.log(res); // 只會調用 onFulfilled }, err => { console.log(err); // 不會調用 rejected }) // fulfilled
案例二:
const promise = new Promise((resolve, reject) => { reject('rejected...'); // 狀態由 pending --> rejected }); promise.then(res => { console.log(res); // 不會調用 onFulfilled }, err => { console.log(err); // 只會調用 rejected }) // rejected
案例三:
const promise1 = new Promise((resolve, reject) => { resolve('fulfilled...'); // 狀態先由 pending --> rejected reject('rejected...'); // 狀態不會再變 pending --> rejected }); promise1.then(res => { console.log(res); // 只會調用 onFulfilled }, err => { console.log(err); // 不會調用 rejected }) // fulfilled const promise2 = new Promise((resolve, reject) => { reject('rejected...'); // 狀態先由 pending --> rejected resolve('fulfilled...'); // 狀態不會再變 pending --> rejected }); promise2.then(res => { console.log(res); // 不會調用 onFulfilled }, err => { console.log(err); // 只會調用 rejected }) // rejected
通過以上三個案例,我們發現,promise狀態一旦由pending提升為fulfilled或rejected就不會再改變了,只能單向變化;并且then方法中只會調用其中一個方法(成功的回調或者事變的回調),不會二者都調用。
Promise.resolve(1) .then(2) .then(Promise.resolve(3)) .then(console.log) // 輸出 1
Promise的then方法的參數期望是函數,傳入非函數則會發生值穿透。
-
Promise.prototype.catch(onRejected)
: catch在鏈式寫法中可以捕獲前面then中發送的異常。promise.then(res => { console.log('haha'); return 'haha' }).then(res => { throw new Error('hehe'); // 此處錯誤被捕獲 }).catch(err => { console.log(err) }) promise.then(res => { console.log('haha'); throw new Error('haha'); // 此處錯誤被捕獲 }).then(res => { console.log('hehe'); throw new Error('hehe'); // 此處不會執行 }).catch(err => { console.log(err) })
catch一旦捕獲到了一個then中的錯誤,后續的then方法就不會再執行下去了。其實,catch相當于then(null,onRejected),前者只是后者的語法糖而已,使用catch方法替代更好吧。
-
Promise.prototype.finally(onFinally)
: 無論當前promise的狀態是完成(fulfilled)還是失敗(rejected)都會執行的回調,并且返回新的promise對象。promise.then(res => { throw new Error('test'); }).catch(errr => { console.log(err); }).finally(()=>{ // 返回狀態為(resolved 或 rejected)都會執行 console.log('當前promise執行結束') });
let isLoading = true; fetch(myRequest).then(function(response) { let contentType = response.headers.get("content-type"); if(contentType && contentType.includes("application/json")) { return response.json(); } throw new TypeError("Oops, we haven't got JSON!"); }) .then(function(json) { /* process your JSON further */ }) .catch(function(error) { console.log(error); }) .finally(function() { isLoading = false; });
Promise.prototype.constructor
: 返回被創建的實例函數. 默認為 Promise 函數.
2.3 promise靜態方法
所謂靜態方法(static method)就是可以直接通過對象調用,而不用實例化的方法,即Promise.methodName()
;
Promise.resolve(value)
:返回一個狀態由給定value(可以是普通值,也可以是promise對象)決定的Promise對象。如果value是thenable(即,帶有then方法的對象),返回的Promise對象的最終狀態由then方法執行決定,否則返回成功的promise對象(狀態為fulfilled)Promise.reject(reason)
:返回一個狀態為失敗(rejected)的Promise對象,并將給定的失敗信息傳遞給對應的處理方法-
Promise.all(iterable)
: 一損俱損,只要一個失敗,就返回新的pomise對象,否則就等待所有的狀態提升為fulfilied。“團結”用于處理多個promise對象的狀態集合,集合中可以傳入常數,當做成功的promise返回值。當集合中一旦有一個promise轉改變為rejected,all的狀態就變為rejected:
const p1 = Promise.resolve(1); const p2 = Promise.resolve(2); const p3 = Promise.reject(3); const p4 = Promise.resolve(4); // 沒有錯誤 Promise.all([p1,p2,33, p4]).then(res => { console.log(res); // [1,2,33,4] }) // 有錯誤 Promise.all([p1,p2,p3, 33, p4]).then(res => { console.log(res); // 只要發現一個錯誤,就不會執行fulfilled方法 }).catch(err => { console.log(err); // 執行錯誤,輸出3 })
當集合中所有的promise狀態都為fulfilled時,必須等待所有的promise執行完畢,即所有的promise對象狀態都提升為fulfilled再返回新dePromise對象,執行then方法。
-
Promise.race(iterable)
: 一榮俱榮,只要一個狀態改變(fulfilled或reject),就返回新的promise對象。“賽跑”用于處理多個promise對象的狀態集合,集合中可以傳入常數,當做成功的promise返回值。當集合中一旦有一個promise轉改變為fulfilled或rejected,all的狀態就提升:
Promise.race([p1,p2,33,p4]).then(res=> { console.log(res); // 只會輸出其中一個狀態成功改變的,輸出:1 }); Promise.race([p1,p2,p3, 33,p4]).then(res=> { console.log(res); // 雖然p3出錯了,但是p1狀態先提升,只會執行狀態最先提升的,輸出:1 }).catch(err => { console.log(err);// 這里不會執行 });
-
*
Promise.any(iterable)
: 處于提案中。。。只要參數實例有一個變成fulfilled
狀態,包裝實例就會變成fulfilled
狀態;如果所有參數實例都變成rejected
狀態,包裝實例就會變成rejected
狀態。手動實現如下:
Promise.any = function(arr) { var self = this; return new Promise(function(resolve, reject){ var errors = []; var count = 0; var len = arr.length; for(var i=0;i<len;i++){ // 只要有一個實例狀態變為fulfilled,新的Promise狀態就會改變為fulfilled self.resolve(arr[i]).then(function(res){ resolve(res); }, function(err){ errors[count] = err; count++; // 否則等待所有的rejected,新的Promise狀態才會改變為rejected if(count === len){ reject(errors) } }) } }) }
-
*
Promise.allSettled(iterable)
: ES2020將實現。。。 只有等到所有這些參數實例都返回結果,不管是fulfilled
還是rejected
,包裝實例才會結束。不關心結果,只關心有沒有執行完畢,該方法返回的新的 Promise 實例,一旦結束,狀態總是fulfilled
,不會變成rejected
手動實現如下:
Promise.allSettled = function(arr){ var results = []; var len = arr.length; for(var i=0;i<len;i++){ this.resolve(arr[i]).then(function(res){ results.push({status:'fulfilled', value: res}); }, function(err){ results.push({status:'rejected', value: err}); }) } // 一旦結束,狀態總是`fulfilled`,不會變成`rejected` return new Promise(function(resolve, reject) { resolve(results) }) }
-
*
Promise.try()
:事實上,Promise.try
就是模擬try
代碼塊,就像promise.catch
模擬的是catch
代碼塊。需求的提出:
不論函數
f
是同步函數還是異步操作,但是想用 Promise 來處理它:Promise.resolve().then(f).catch()
? 但是,如果
f
是同步函數,那么它會在本輪事件循環的末尾執行。const f = () => console.log('now'); Promise.resolve().then(f); console.log('next'); // next // now
有沒有方法,讓同步函數同步執行,異步函數異步執行,并且讓它們具有統一的 API捕獲錯誤:
const f = () => console.log('now'); ( () => new Promise( resolve => resolve(f()) ) )(); console.log('next'); // now // next // 或者 (async () => f())(); console.log('next'); // now // next
用
Promise.try
替代上面的代碼:const f = () => console.log('now'); Promise.try(f); console.log('next'); // now // next
手動實現如下:
Promise.try = function(fn){ if(typeof fn !== 'function') return; return new Promise(function(resolve, reject) { return resolve(fn()); }) }
2.3 promise屬性
-
Promise.length
:length屬性,其值總是1(構造器參數的數目) -
Promise.prototype
:Promise
構造器的原型
3. JS事件執行機制(Event Loop)
為了更好地理解promise的應用,先需要理解JS執行機制。
運行時概念:
函數調用形成了一個棧幀 (call stack)。
對象被分配在一個堆(heap)中,即用以表示一大塊非結構化的內存區域。
-
所有的異步操作都會進入隊列(queue)中等待被執行。
[圖片上傳失敗...(image-11921a-1570631381694)]
一個 JavaScript 運行時包含了一個待處理的消息隊列。每一個消息都關聯著一個用以處理這個消息的函數。
函數的處理會一直進行到執行棧再次為空為止;然后事件循環將會處理隊列中的下一個消息(如果還有的話)。
事件循環機制:
[圖片上傳失敗...(image-d11d0d-1570631381694)]
JS執行時,將任務分為同步任務和異步任務,同步任務都在主線程上執行(主代碼塊),形成一個執行棧,異步任務會被加入到任務隊列里面。
任務隊列中的任務分為兩種任務類型:macrotask和microtask,任務隊列里面微任務優先于宏任務執行,先執行完任務隊列里面所有的微任務,然后再執行任務隊列里面的宏任務。
宏任務:script(主代碼塊),
setTimeout,
setInterval
,setImmediate
,I/O
, UI rendering,MessageChannel、setImmediate(Node.js 環境)。每次執行棧執行的代碼就是一個宏任務-
微任務:
process.nextTick
(nodejs相關),Promise
,Object.observer
,MutationObserver
。在當前宏任務(主線程) 執行結束后立即執行的任務[圖片上傳失敗...(image-6695ba-1570631381694)]
JS運行機制:
執行棧中的宏任務(棧中沒有就從事件隊列中獲取),一般首先執行普通代碼塊(script)
執行過程中如果遇到微任務,就將它添加到微任務隊列中
當前宏任務執行完畢后,立即執行當前微任務隊列中的所有微任務(依次執行)
當前宏任務執行完畢,開始檢查渲染,然后GUI線程接管渲染
-
渲染完畢后,JS線程繼續接管,開始下一個宏任務(從事件隊列中獲取),以此循環...
JS運行機制
參考:
4. promise測試題
4.1 示例一:
new Promise(resolve => {
// promise構造函數里面是同步代碼區,和普通代碼塊一樣
console.log(5);
resolve(1);
Promise.resolve().then(() => {
console.log(2)
});
console.log(4)
}).then(t => {
console.log(t)
}).catch(()=>{
console.log(6);
}).finally(() =>{
console.log(0);
});
console.log(3);
<details>
<summary>輸出結果</summary>
<pre>5 4 3 2 1 0</pre>
</details>
4.2 示例二:
console.log('script start'); // 宏任務1
setTimeout(function() {
console.log('setTimeout'); // 宏任務2
}, 0);
Promise.resolve().then(function() {
console.log('promise1');// 微任務1
}).then(function() {
console.log('promise2'); // 微任務1
});
console.log('script end'); // 宏任務1
<details>
<summary>輸出結果</summary>
<pre>
script start
script end
promise1
promise2
setTimeout
</pre>
</details>
4.3 示例三:
let p1 = new Promise((resolve,reject)=>{
let num = 6
if(num<5){
console.log('resolve1')
resolve(num)
}else{
console.log('reject1')
reject(num)
}
})
p1.then((res)=>{
console.log('resolve2')
console.log(res)
},(rej)=>{
console.log('reject2')
let p2 = new Promise((resolve,reject)=>{
if(rej*2>10){
console.log('resolve3')
resolve(rej*2)
}else{
console.log('reject3')
reject(rej*2)
}
})
return p2
}).then((res)=>{
console.log('resolve4')
console.log(res)
},(rej)=>{
console.log('reject4')
console.log(rej)
})
<details>
<summary>輸出結果</summary>
<pre>
reject1
reject2
resolve3
resolve4
12
</pre>
</details>
4.4 示例四:
<iframe height="302" style="width: 100%;" scrolling="no" title="ES6-promise-demo3" src="https://codepen.io/keekuun/embed/abbzjbM?height=302&theme-id=0&default-tab=js,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a >ES6-promise-demo3</a> by Keekuun
(<a >@keekuun</a>) on <a >CodePen</a>.
</iframe>
<details>
<summary>點擊內部輸出</summary>
<pre>
click
promise
mutate
click
promise
mutate
timeout
timeout
</pre>
</details>
4.5 示例五:
async function a1 () { // async關鍵字
console.log('a1 start')
await a2() // await關鍵字
console.log('a1 end')
}
async function a2 () {
console.log('a2')
}
console.log('script start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve().then(() => {
console.log('promise1')
})
a1()
let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})
promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')
<details>
<summary>輸出結果</summary>
<pre>
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout
</pre>
</details>
4.6 示例五:
async function test() {
console.log('test start');
await undefined;
console.log('await 1');
await new Promise(resolve => {
console.log('promise in async');
resolve();
});
console.log('await 2');
}
test();
new Promise((resolve) => {
console.log('promise');
resolve();
})
.then(() => {console.log(1)})
.then(() => {console.log(2)})
.then(() => {console.log(3)})
.then(() => {console.log(4)});
<details>
<summary>輸出結果</summary>
<pre>
test start
promise
await 1
promise in async
1
await 2
2
3
4
</pre>
</details>
5. 手寫promise
- 使用ES5之前的語法實現:
(function () {
// 判斷function
function isFunction(fn) {
return typeof fn === 'function';
}
// 狀態 pending、fulfilled、rejected
var PENDING = 'pending';
var FULFILLED = 'fulfilled';
var REJECTED = 'rejected';
// 構造方法
var Kromise = function (handle) {
// 當前狀態
this._status = PENDING;
// 添加成功回調隊列
this._fulfilledQueue = [];
// 添加失敗回調隊列
this._rejectedQueue = [];
// 引用當前this對象
var self = this;
if (!isFunction(handle)) {
throw new Error('Parameter handle is not a function!')
}
// 添加resolve時執行的函數
function _resolve(val) {
var run = function () {
if (self._status !== PENDING) return;
// 依次執行成功隊列中的函數,并清空隊列
var runFulfilled = function (res) {
var resolve;
while (resolve = self._fulfilledQueue.shift()) { // 出棧
resolve(res);
}
};
// 依次執行失敗隊列中的函數,并清空隊列
var runRejected = function (err) {
var reject;
while (reject = self._rejectedQueue.shift()) { // 出棧
reject(err);
}
};
/* 如果resolve的參數為Kromise對象,則必須等待該Kromise對象狀態改變后,
* 當前Kromise的狀態才會改變,且狀態取決于參數Kromise對象的狀態
*/
if (val instanceof Kromise) {
val.then(function (value) {
self._status = FULFILLED;
self._value = value;
runFulfilled(value)
}, function (err) {
self._status = REJECTED;
self._value = err;
runRejected(err);
})
} else {
self._status = FULFILLED;
self._value = val;
runFulfilled(val);
}
};
// 為了支持同步的Promise,這里采用異步調用
setTimeout(run, 0)
}
// 添加reject時執行的函數
function _reject(err) {
var run = function () {
if (self._status !== PENDING) return;
// 依次執行成功隊列中的函數,并清空隊列
self._status = REJECTED;
self._value = err;
var reject;
while (reject = self._fulfilledQueue.shift()) { // 出棧
reject(err);
}
};
// 為了支持同步的Promise,這里采用異步調用
setTimeout(run, 0)
}
// 執行handle,捕獲異常
try {
handle(_resolve.bind(this), _reject.bind(this));
} catch (e) {
_reject(e);
}
};
// 屬性
Kromise.length = 1;
// 實例方法
// 實現then方法
Kromise.prototype.then = function (onFulfilled, onRejected) {
var self = this;
// 返回一個新的Kromise對象
return new Kromise(function (onFulfilledNext, onRejectedNext) {
// 成功時的回調
var fulfilled = function (val) {
try {
// 如果不是函數,值穿透
if (!isFunction(onFulfilled)) {
onFulfilledNext(val)
} else {
var res = onFulfilled(val);
// 如果當前回調函數返回Kromise對象,必須等待其狀態改變后在執行下一個回調
if (res instanceof Kromise) {
res.then(onFulfilledNext, onRejectedNext);
} else {
//否則會將返回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
onFulfilledNext(res);
}
}
} catch (e) {
// 如果函數執行出錯,新的Kromise對象的狀態為失敗
onRejectedNext(e);
}
};
// 失敗時的回調
var rejected = function (err) {
try {
if (!isFunction(onRejected)) {
onRejectedNext(err)
} else {
var res = onRejected(err);
if (res instanceof Kromise) {
res.then(onFulfilledNext, onRejectedNext);
} else {
onFulfilledNext(res);
}
}
} catch (e) {
onRejectedNext(e)
}
};
switch (self._status) {
// 當狀態為pending時,將then方法回調函數加入執行隊列等待執行
case PENDING:
self._fulfilledQueue.push(fulfilled);
self._rejectedQueue.push(rejected);
break;
// 當狀態已經改變時,立即執行對應的回調函數
case FULFILLED:
fulfilled(self._value);
break;
case REJECTED:
rejected(self._value);
break;
}
});
};
// 實現catch方法
Kromise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
// 實現finally方法
Kromise.prototype.finally = function (onFinally) {
return this.then(function (value) {
Kromise.resolve(onFinally()).then(function () {
return value;
})
}, function (err) {
Kromise.resolve(onFinally()).then(function () {
throw new Error(err);
})
})
};
// 靜態方法
// 實現resolve方法
Kromise.resolve = function (value) {
// 如果參數是Kromise實例,直接返回這個實例
if (value instanceof Kromise) {
return value;
}
return new Kromise(function (resolve) {
resolve(value)
})
};
// 實現reject方法
Kromise.reject = function (value) {
return new Kromise(function (resolve, reject) {
reject(value)
})
};
// 實現all方法
Kromise.all = function (arr) {
var self = this;
return new Kromise(function (resolve, reject) {
var values = [];
for (var i = 0, len = arr.length; i < len; i++) {
// 數組參數如果不是Kromise實例,先調用Kromise.resolve
self.resolve(arr[i]).then(function (res) {
values.push(res);
// 所有狀態都變成fulfilled時返回的Kromise狀態就變成fulfilled
if (values.length === arr.length) {
resolve(values);
}
}, function (e) {
// 有一個被rejected時返回的Kromise狀態就變成rejected
reject(e);
})
}
})
};
// 實現race方法
Kromise.race = function (arr) {
var self = this;
return new Kromise(function (resolve, reject) {
for (var i = 0, len = arr.length; i < len; i++) {
// 只要有一個實例率先改變狀態,新的Kromise的狀態就跟著改變
self.resolve(arr[i]).then(function (res) {
resolve(res);
}, function (err) {
reject(err);
})
}
})
};
// 實現any方法
Kromise.any = function (arr) {
var self = this;
return new Kromise(function (resolve, reject) {
var count = 0;
var errors = [];
for (var i = 0, len = arr.length; i < len; i++) {
// 只要有一個實例狀態變為fulfilled,新的Kromise狀態就會改變為fulfilled
self.resolve(arr[i]).then(function (res) {
resolve(res);
}, function (err) {
errors[count] = err;
count++;
// 否則等待所有的rejected,新的Kromise狀態才會改變為rejected
if (count === arr.length) {
reject(errors);
}
})
}
})
};
// 實現allSettled方法
Kromise.allSettled = function (arr) {
var results = [];
var len = arr.length;
for (var i = 0; i < len; i++) {
this.resolve(arr[i]).then(function (res) {
results.push({status: FULFILLED, value: res});
}, function (err) {
results.push({status: REJECTED, value: err});
})
}
// 一旦結束,狀態總是`fulfilled`,不會變成`rejected`
return new Kromise(function (resolve, reject) {
resolve(results)
})
};
// 實現try方法
Kromise.try = function (fn) {
if (!isFunction(fn)) return;
return new Kromise(function (resolve, reject) {
return resolve(fn());
})
};
// 掛載
window.Kromise = Kromise;
})();
- 使用ES6 class語法實現
// 判斷變量否為function
const isFunction = variable => typeof variable === 'function'
// 定義Promise的三種狀態常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (handle) {
if (!isFunction(handle)) {
throw new Error('MyPromise must accept a function as a parameter')
}
// 添加狀態
this._status = PENDING
// 添加狀態
this._value = undefined
// 添加成功回調函數隊列
this._fulfilledQueues = []
// 添加失敗回調函數隊列
this._rejectedQueues = []
// 執行handle
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// 添加resovle時執行的函數
_resolve (val) {
const run = () => {
if (this._status !== PENDING) return
// 依次執行成功隊列中的函數,并清空隊列
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value)
}
}
// 依次執行失敗隊列中的函數,并清空隊列
const runRejected = (error) => {
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(error)
}
}
/* 如果resolve的參數為Promise對象,則必須等待該Promise對象狀態改變后,
當前Promsie的狀態才會改變,且狀態取決于參數Promsie對象的狀態
*/
if (val instanceof MyPromise) {
val.then(value => {
this._value = value
this._status = FULFILLED
runFulfilled(value)
}, err => {
this._value = err
this._status = REJECTED
runRejected(err)
})
} else {
this._value = val
this._status = FULFILLED
runFulfilled(val)
}
}
// 為了支持同步的Promise,這里采用異步調用
setTimeout(run, 0)
}
// 添加reject時執行的函數
_reject (err) {
if (this._status !== PENDING) return
// 依次執行失敗隊列中的函數,并清空隊列
const run = () => {
this._status = REJECTED
this._value = err
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(err)
}
}
// 為了支持同步的Promise,這里采用異步調用
setTimeout(run, 0)
}
// 添加then方法
then (onFulfilled, onRejected) {
const { _value, _status } = this
// 返回一個新的Promise對象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 封裝一個成功時執行的函數
let fulfilled = value => {
try {
if (!isFunction(onFulfilled)) {
onFulfilledNext(value)
} else {
let res = onFulfilled(value);
if (res instanceof MyPromise) {
// 如果當前回調函數返回MyPromise對象,必須等待其狀態改變后在執行下一個回調
res.then(onFulfilledNext, onRejectedNext)
} else {
//否則會將返回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函數執行出錯,新的Promise對象的狀態為失敗
onRejectedNext(err)
}
}
// 封裝一個失敗時執行的函數
let rejected = error => {
try {
if (!isFunction(onRejected)) {
onRejectedNext(error)
} else {
let res = onRejected(error);
if (res instanceof MyPromise) {
// 如果當前回調函數返回MyPromise對象,必須等待其狀態改變后在執行下一個回調
res.then(onFulfilledNext, onRejectedNext)
} else {
//否則會將返回結果直接作為參數,傳入下一個then的回調函數,并立即執行下一個then的回調函數
onFulfilledNext(res)
}
}
} catch (err) {
// 如果函數執行出錯,新的Promise對象的狀態為失敗
onRejectedNext(err)
}
}
switch (_status) {
// 當狀態為pending時,將then方法回調函數加入執行隊列等待執行
case PENDING:
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
// 當狀態已經改變時,立即執行對應的回調函數
case FULFILLED:
fulfilled(_value)
break
case REJECTED:
rejected(_value)
break
}
})
}
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
// 添加靜態resolve方法
static resolve (value) {
// 如果參數是MyPromise實例,直接返回這個實例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
// 添加靜態reject方法
static reject (value) {
return new MyPromise((resolve ,reject) => reject(value))
}
// 添加靜態all方法
static all (list) {
return new MyPromise((resolve, reject) => {
/**
* 返回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 數組參數如果不是MyPromise實例,先調用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 所有狀態都變成fulfilled時返回的MyPromise狀態就變成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一個被rejected時返回的MyPromise狀態就變成rejected
reject(err)
})
}
})
}
// 添加靜態race方法
static race (list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一個實例率先改變狀態,新的MyPromise的狀態就跟著改變
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
finally (cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason => MyPromise.resolve(cb()).then(() => { throw reason })
);
}
}