Promise的特性及實現原理


Promise對象的特性

要實現Promise對象首先我們要了解Promise擁有哪些特性,簡單概括為以下幾點
1、Promise有三種狀態:pending(進行中)、fulfilled(已成功)、rejected(已失敗)
2、Promise對象接受一個回調函數作為參數, 該回調函數接受兩個參數,分別是成功時的回調resolve和失敗時的回調reject;另外resolve的參數除了正常值以外, 還可能是一個Promise對象的實例;reject的參數通常是一個Error對象的實例。
3、then方法返回一個新的Promise實例,並接收兩個參數onResolved(fulfilled狀態的回調);
onRejected(rejected狀態的回調,該參數可選)
4、catch方法返回一個新的Promise實例
5、finally方法不管Promise狀態如何都會執行,該方法的回調函數不接受任何參數
6、Promise.all()方法將多個多個Promise實例,包裝成一個新的Promise實例,該方法接受一個由Promise對象
組成的數組作為參數(Promise.all()方法的參數可以不是數組,但必須具有Iterator接口,且返回的每個成員都是Promise實例),注意參數中只要有一個實例觸發catch方法,都會觸發Promise.all()方法返回的新的實例的catch方法,如果參數中的某個實例本身調用了catch方法,將不會觸發Promise.all()方法返回的新實例的catch方法
7、Promise.race()方法的參數與Promise.all方法一樣,參數中的實例只要有一個率先改變狀態就會將該實例的狀態傳給Promise.race()方法,並將返回值作為Promise.race()方法產生的Promise實例的返回值
8、Promise.resolve()將現有對象轉為Promise對象,如果該方法的參數為一個Promise對象,Promise.resolve()將不做任何處理;如果參數thenable對象(即具有then方法),Promise.resolve()將該對象轉為Promise對象並立即執行then方法;如果參數是一個原始值,或者是一個不具有then方法的對象,則Promise.resolve方法返回一個新的Promise對象,狀態為fulfilled,其參數將會作為then方法中onResolved回調函數的參數,如果Promise.resolve方法不帶參數,會直接返回一個fulfilled狀態的 Promise 對象。需要注意的是,立即resolve()的 Promise 對象,是在本輪“事件循環”(event loop)的結束時執行,而不是在下一輪“事件循環”的開始時。

setTimeout(function () { console.log('three'); }, 0); Promise.resolve().then(function () { console.log('two'); }); console.log('one'); // one // two // three 

9、Promise.reject()同樣返回一個新的Promise對象,狀態為rejected,無論傳入任何參數都將作為reject()的參數
以上就是Promise對象的一些基本特性,下面我們根據Promise對象的特性,自己來實現一個簡單的Promise對象

第一步、Promise對象用三種狀態分別是:pending、fulfilled、rejected。
resolve的參數為一個Promise對象的實例的時時候該實例的resolve的參數即為外層Promise對象then方法中onResolved方法的參數,reject的參數即為外層Promise對象then方法(或catch方法)中onRejected方法的參數

function timeout2() { return new Promise((resolve, reject) => { setTimeout(() => { try { a // 此處a未定義,如果注釋掉這里即正常執行 resolve(200) } catch (e) { reject(e) } }, 1000) }) } function timeout(time) { return new Promise((resolve, reject) => { setTimeout(() => { try { resolve(timeout2()) } catch (e) { reject(e) } }, time) }) } let p = timeout(1000); p.then(res => { console.log(res); // 200 }).catch(err => { console.log(err); // ReferenceError: a is not defined }) 

由上面的例子我們可以定義一個基本框架:

/* Promise構造函數接收一個executor函數, executor函數執行完同步或異步操作后,調用它的兩個參數resolve和reject 如果操作成功,調用resolve並傳入value 如果操作失敗,調用reject並傳入reason */ class MyPromise { constructor(executor) { if(typeof executor !== 'function') { throw new Error('MyPromise must accept a function as a parameter') } // Promise當前的狀態 this.status = 'pending' // Promise的值 this.data = undefined // Promise resolve時的回調函數集,因為在Promise結束之前有可能有多個回調添加到它上面 this.onResolvedCallback = [] // Promise reject時的回調函數集,因為在Promise結束之前有可能有多個回調添加到它上面 this.onRejectedCallback = [] /* 考慮到執行executor的過程中有可能出錯,所以我們用try/catch塊給包起來, 並且在出錯后以catch到的值reject掉這個Promise,另外因為resolve和reject在外部調用故需要綁定this */ try { executor(this.resolve.bind(this), this.reject.bind(this)) } catch (err) { this.reject(err) } } resolve(value) { // 成功時將狀態改為fulfilled if(this.status === 'padding') { // 如果傳入的值是一個promise實例,則必須等待該Promise對象狀態改變后, // 當前Promsie的狀態才會改變,且狀態取決於參數Promsie對象的狀態 if(value instanceof MyPromise) { value.then(res => { this.data = res this.status = 'fulfilled' //執行resolve的回調函數,將value傳遞到callback中 this.onResolvedCallback.forEach(callback => callback(res)) }, err => { this.data = err this.status = 'rejected' //執行reject的回調函數,將reason傳遞到callback中 this.onRejectedCallback.forEach(callback => callback(err)) }) } else { this.status = 'fulfilled'; this.data = value; //執行resolve的回調函數,將value傳遞到callback中 this.onResolvedCallback.forEach(callback => callback(value)) } } } reject(reason) { // 失敗時將狀態改為rejected if(this.status === 'padding') { this.status = 'rejected' this.data = reason; // 觸發所有的回調函數 this.onRejectedCallback.forEach(item => { item(reason) }) } } } 

第二步、實現then方法
Promise實例的then方法返回一個新的Promise實例,並接收兩個參數onResolved(fulfilled狀態的回調); onRejected(rejected狀態的回調,該參數可選);

// then方法接收兩個參數,onResolved,onRejected,分別為Promise成功或失敗后的回調 class MyPromise { // .... then(onResolved, onRejected) { // 根據標准,如果then的參數不是function,則我們需要忽略它,此處以如下方式處理 onResolved = typeof onResolved === 'function' ? onResolved : value => {} onRejected = typeof onRejected === 'function' ? onRejected : reason => {} if (this.status === 'fulfilled') { return new MyPromise((resolve, reject) => { }) } if (this.status === 'rejected') { return new MyPromise((resolve, reject) => { }) } if (this.status === 'pending') { return new MyPromise((resolve, reject) => { }) } } } 

當我們在鏈式調用Promise實例的時候,當一個實例的then方法返回另一個實例,第二個then方法指定的回調函數,就會等待這個新的Promise對象狀態發生變化。如果變為fulfilled,就調用第一個回調函數(即onResolved),如果狀態變為rejected,就調用第二個回調函數(即onRejected)。

let p1 = new Promise((resolve,reject) => { setTimeout(function(){ try { resolve(1) } catch (e) { reject(e) } }, 100) }) let p2 = new Promise((resolve,reject) => { setTimeout(function(){ try { resolve(2) } catch (e) { reject(e) } }, 100) }) p1.then(res => { console.log(res); // 1 return p2 }).then(res => { console.log(res); // 2 }).catch(err => { }) 

第三步、完善then方法

根據上面的例子我們來補充then方法里面的內容

// then方法接收兩個參數,onResolved,onRejected,分別為Promise成功或失敗后的回調 class MyPromise { // .... then(onResolved, onRejected) { // 根據標准,如果then的參數不是function,則我們需要忽略它,此處以如下方式處理 onResolved = typeof onResolved === 'function' ? onResolved : value => value onRejected = typeof onRejected === 'function' ? onRejected : reason => reason // 如果promise1(此處即為this)的狀態已經確定並且是resolved,我們調用onResolved // 因為考慮到有可能throw,所以我們將其包在try/catch塊里 if (this.status === 'fulfilled') { return new MyPromise((resolve, reject) => { try { let result = onResolved(this.data) // 如果onResolved的返回值是一個Promise對象,直接取它的結果做為返回promise實例的結果 if(result instanceof MyPromise) { result.then(resolve, reject) } resolve(result) // 否則,以它的返回值做為返回promise實例的結果 } catch (e) { reject(e) } }) } // rejected狀態的處理方法與上面基本一致 if (this.status === 'rejected') { return new MyPromise((resolve, reject) => { try { let result = onRejected(this.data) // 如果onRejected的返回值是一個Promise對象,直接取它的結果做為返回promise實例的結果 if(result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) } if (this.status === 'pending') { /** * 如果當前的Promise還處於pending狀態,我們並不能確定調用onResolved還是onRejected, * 只能等到Promise的狀態確定后,才能確實如何處理。 * 所以我們需要把以上兩種情況的處理邏輯做為callback放入promise1(此處即this)的回調數組里 * 具體邏輯也與上面類似 */ return new MyPromise((resolve, reject) => { this.onResolvedCallback.push((value) => { try { let result = onResolved(this.data); if (result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) this.onRejectedCallback.push(reason => { try { let result = onRejected(this.data) if (result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) }) } } } 

第四步、完成catch方法
以上我們就實現了一個promise對象的基本功能,最后加上catch方法完整代碼如下


/* Promise構造函數接收一個executor函數, executor函數執行完同步或異步操作后,調用它的兩個參數resolve和reject 如果操作成功,調用resolve並傳入value 如果操作失敗,調用reject並傳入reason */ class MyPromise { constructor(executor) { if(typeof executor !== 'function') { throw new Error('MyPromise must accept a function as a parameter') } // Promise當前的狀態 this.status = 'pending' // Promise的值 this.data = undefined // Promise resolve時的回調函數集,因為在Promise結束之前有可能有多個回調添加到它上面 this.onResolvedCallback = [] // Promise reject時的回調函數集,因為在Promise結束之前有可能有多個回調添加到它上面 this.onRejectedCallback = [] /* 考慮到執行executor的過程中有可能出錯,所以我們用try/catch塊給包起來, 並且在出錯后以catch到的值reject掉這個Promise,另外因為resolve和reject在外部調用故需要綁定this */ try { executor(this.resolve.bind(this), this.reject.bind(this)) } catch (err) { this.reject(err) } } resolve(value) { // 成功時將狀態改為fulfilled if(this.status === 'padding') { // 如果傳入的值是一個promise實例,則必須等待該Promise對象狀態改變后, // 當前Promsie的狀態才會改變,且狀態取決於參數Promsie對象的狀態 if(value instanceof MyPromise) { value.then(res => { this.data = res this.status = 'fulfilled' //執行resolve的回調函數,將value傳遞到callback中 this.onResolvedCallback.forEach(callback => callback(res)) }, err => { this.data = err this.status = 'rejected' //執行reject的回調函數,將reason傳遞到callback中 this.onRejectedCallback.forEach(callback => callback(err)) }) } else { this.status = 'fulfilled'; this.data = value; //執行resolve的回調函數,將value傳遞到callback中 this.onResolvedCallback.forEach(callback => callback(value)) } } } reject(reason) { // 失敗時將狀態改為rejected if(this.status === 'padding') { this.status = 'rejected' this.data = reason; // 觸發所有的回調函數 this.onRejectedCallback.forEach(item => { item(reason) }) } } then(onResolved, onRejected) { // 根據標准,如果then的參數不是function,則我們需要忽略它,此處以如下方式處理 onResolved = typeof onResolved === 'function' ? onResolved : value => value onRejected = typeof onRejected === 'function' ? onRejected : reason => reason // 如果promise1(此處即為this)的狀態已經確定並且是resolved,我們調用onResolved // 因為考慮到有可能throw,所以我們將其包在try/catch塊里 if (this.status === 'fulfilled') { return new MyPromise((resolve, reject) => { try { let result = onResolved(this.data) // 如果onResolved的返回值是一個Promise對象,直接取它的結果做為返回promise實例的結果 if(result instanceof MyPromise) { result.then(resolve, reject) } resolve(result) // 否則,以它的返回值做為返回promise實例的結果 } catch (e) { reject(e) } }) } // rejected狀態的處理方法與上面基本一致 if (this.status === 'rejected') { return new MyPromise((resolve, reject) => { try { let result = onRejected(this.data) // 如果onRejected的返回值是一個Promise對象,直接取它的結果做為返回promise實例的結果 if(result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) } if (this.status === 'pending') { /** * 如果當前的Promise還處於pending狀態,我們並不能確定調用onResolved還是onRejected, * 只能等到Promise的狀態確定后,才能確實如何處理。 * 所以我們需要把以上兩種情況的處理邏輯做為callback放入promise1(此處即this)的回調數組里 * 具體邏輯也與上面類似 */ return new MyPromise((resolve, reject) => { this.onResolvedCallback.push((value) => { try { let result = onResolved(this.data); if (result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) this.onRejectedCallback.push(reason => { try { let result = onRejected(this.data) if (result instanceof MyPromise) { result.then(resolve, reject) } } catch (e) { reject(e) } }) }) } } catch(onRejected) { return this.then(null, onRejected) } } 

以上就實現了Promise對象的核心功能,其他的幾個特性很容易實現在此不做贅述,由於本人水平有限,如有錯漏之處請指出斧正。



作者:XZ陽光小熊
鏈接:https://www.jianshu.com/p/b9ec2e3c3ad8
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM