今天這篇文章主要帶領你手寫一個Promise源碼,學完你就會發現:Promise沒有你想象中的那麼難,如果對Promise源碼不是很清楚,還是推薦從頭看,相信你認真從頭看到尾,并且去實際操作了,肯定會有收獲的。
主要是代碼部分有點多,不過好多都是重複的,不必擔心。
Promise出現的原因
promise出現的原因,說白了也就是promise解決了什麼問題,這裡我就簡單說點吧:
Promise 是異步程式設計的一種解決方案,比傳統的解決方案回調函數和事件更合理和更強大,Promise的出現主要解決以下兩個問題:
- 回調地獄: 某個異步操作需要等待之前的操作完成在繼續執行, 當這樣的需求多了以後, 使的代碼進入無盡的嵌套,可讀性降低不好維護。
- 異步和同步之間的聯系問題:當一個同步操作需要等待多個異步操作的結果, 這樣會使得代碼的邏輯變得相對來說比較複雜。
myPromise的實作要點
- Promise 就是一個類,實際上就是ES6提供的一個新的構造函數,
- 通過new建立執行個體,接收一個函數作為參數,并且該函數中的代碼預設是同步代碼,會立即執行
- Promise 有三種狀态,成功resolved,失敗rejected,等待pedding
- Promise 狀态一旦發生改變,就不能在改變,狀态不可逆
- 使用者可自定義成功的資料及失敗的原因
- Promise 執行個體擁有一個then方法。接受兩個參數,一個成功回調,一個失敗回調
- executor執行器在執行過程中,可能會抛出異常 throw new Error(),需要 try catch 捕獲
- 當執行器中的是異步代碼時(如定時器執行resolve),不是立即執行 狀态不會變更,此時then方法中狀态仍然是pending
- 一個 promise 執行個體可以被 then 多次,分别建立一個隊列用來存放成功和失敗回調事件
- 定時器執行時,判斷狀态為 pending,非立即執行,而是先存放 成功/失敗 回調事件
- 等待狀态變為成功或失敗時周遊隊列依次執行對應的onfulfilled和onrejected,并且有執行順序
- Promise 實作鍊式調用,傳回的并不是this,而是一個新的promise執行個體, 因為原有的promise狀态一旦發生改變,就不能再改變,否則不符合規範
- Promise 成功和失敗的回調的傳回值,可以傳遞給下一次的的then
- 傳回的是普通值:不論then是成功還是失敗。隻要是普通值就傳遞到下一次then的成功中。(普通值包括:非錯誤非promise,包括對象)
- 報錯或異常:一定會走到下一次then的失敗中。如果離自己最近的then沒有錯誤處理,會向下找
- 特别注意: return new Error()傳回的是錯誤對象,屬于普通值走下一次then的成功,而throw new Error() 是抛出異常,需要try catch 會走下一次的失敗
- 傳回的是promise:會采用promise的狀态,決定下一次then是成功還是失敗,此處會有遞歸判斷
- 總結: 如果傳回一個普通值,除了promise ,就傳遞給下一個then的成功,如果傳回一個失敗的promise或者抛出異常,會走下一個then的失敗。
- Promise 值的穿透,當一個promise連續then 多次,并且then中沒有傳回任何值時,此時data會穿透至最後一個then中
- 14.1 執行onfulfilled時,先判斷是不是一個function,如果是就直接執行,反之就包一層函數
- 14.2 執行onrejected時,先判斷是不是一個function,如果是就直接執行,反之就抛出失敗異常
- 既然promise是為了解決異步回調函數嵌套問題,那麼可以通過promise的延遲對象來減少一層嵌套關系
- promiseA+ 規範測試
- 聊點規範以外的東東吧~~catch, finally,resolve,reject,all,race,就這麼多
myPromise的實作
下面就來實作以上要點
myPromise — 實作簡單的同步
myPromise.js
// 要點 3: 有三種狀态, 相當于是常量,可以放在外面
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
// 要點 1: Promise 就是一個類
class Promise {
// 要點 2: 接收一個函數executor作為參數,立即執行
constructor(executor) {
this.status = PENDING
this.value = undefined // 要點 5: 使用者可自定義 成功的資料
this.reason = undefined // 要點 5: 使用者可自定義 失敗的原因
let resolve = (value) => {
// 要點 4: 狀态不可逆, 隻有PENDING時,才可以改變狀态
if (this.status === PENDING) {
this.value = value // 成功的資料
this.status = RESOLVED // 狀态置為 RESOLVED
}
}
let reject = (reason) => {
// 要點 4: 狀态不可逆, 隻有PENDING時,才可以改變狀态
if (this.status === PENDING) {
this.reason = reason // 失敗的原因
this.status = REJECTED // 狀态置為 REJECTED
}
}
// 要點 7: 錯誤處理,抛出異常 throw new Error()
try {
executor(resolve, reject) // 立即執行
} catch(e) { // 抛出異常 直接失敗
reject(e)
}
}
// 要點 6: 擁有一個then方法(執行個體上的方法)。接受兩個參數回調函數
then(onfulfilled, onrejected) {
if(this.status === RESOLVED){
onfulfilled(this.value)
}
if(this.status === REJECTED){
onrejected(this.reason)
}
}
}
// Commonjs規範導出子產品
module.exports = Promise
test.js 簡單的同步操作驗證
// 使用自己的promise,注釋該行代碼,就是原生promise
let Promise = require('./1.myPromise')
let p = new Promise((resolve, reject) => {
resolve('成功')
// throw new Error('失敗了')
// reject('失敗')
})
p.then((data) => {
console.log(data)
}, (err) => {
console.log(err)
})
myPromise — 增加異步功能
在實作了簡單的同步操作後,再來看看異步代碼的執行,還是使用上面 test 中的案例,簡單的改造一下,寫一個定時器模拟異步環境,test.js:增加異步功能。
let Promise = require('./1.myPromise')
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('成功')
},1000)
})
p.then((data) => {
console.log(data,1)
}, (err) => {
console.log(err)
})
// 一個執行個體可以then多次,執行結果會是一樣的,因為狀态已經固定
p.then((data) => {
console.log(data,2)
}, (err) => {
console.log(err)
})
myPromise.js
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
// 要點 9: 分别建立一個`隊列用來存放成功和失敗回調事件
this.onfulfilledCallbacks = []
this.onrejectedCallbacks = []
let resolve = (value) => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onfulfilledCallbacks.forEach(fn => fn()) // 要點 11: 狀态變為成功時周遊隊列依次執行
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onrejectedCallbacks.forEach(fn => fn()) // 要點 11: 狀态變為失敗時周遊隊列依次執行
}
}
try {
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
then(onfulfilled, onrejected) {
if(this.status === RESOLVED){
onfulfilled(this.value)
}
if(this.status === REJECTED){
onrejected(this.reason)
}
// console.log(this.status) // PENDING
// 要點 8: 定時器執行resolve時,狀态仍然是pending
if(this.status === PENDING){
// 要點 10: 一個 promise 執行個體可以被 then 多次,存放 成功回調事件
this.onfulfilledCallbacks.push(() => {
onfulfilled(this.value)
})
// 要點 10: 一個 promise 執行個體可以被 then 多次,存放 失敗回調事件
this.onrejectedCallbacks.push(() => {
onrejected(this.reason)
})
}
}
}
// Commonjs規範導出子產品
module.exports = Promise
myPromise — 鍊式調用(重難點,不好了解)
鍊式調用這一部分,比較繞,不太好了解,我的老師鼓勵我,說:“書讀百遍,其義自現”,哈哈哈,現在用來鼓勵大家吧!!!test.js。
let Promise = require('./1.myPromise')
let p = new Promise((resolve, reject) => {
resolve('成功')
})
// 可以分别注釋用例代碼,進行效果示範
p.then((data) => {
return data // 用例1. 傳回的是普通值: ‘成功’
// throw new Error() // 用例2. 報錯或異常: 抛出異常,會直接走下一次then的失敗
// return new Error() // 用例3. 傳回的是普通值: error對象,需要特别注意
// return new Promise((s,j)=>s(1)) // 用例4. 傳回的是promise: 會傳遞目前promise的已成功結果
// return new Promise((s,j)=>j(1)) // 用例5. 傳回的是promise: 會傳遞目前promise的已失敗結果
}).then((data) => {
console.log(data,2) // 執行結果:用例1, 用例3, 用例4
}, (err) => {
console.log(err,3) // 執行結果:用例2, 用例5
})
// 1. 傳回的是普通值:不論then是成功還是失敗。隻要是普通值就傳遞到下一次then的成功中。(普通值包括:非錯誤非promise,包括對象)
// 2. 報錯或異常:一定會走到下一次then的失敗中。如果離自己最近的then沒有錯誤處理,會向下找
// 3. 特别注意: return new Error()傳回的是錯誤對象,屬于普通值走下一次then的成功,而throw new Error() 是抛出異常,會走下一次的失敗
// 4. 傳回的是promise:會采用promise的狀态,決定下一次then是成功還是失敗,此處會有遞歸判斷
// 5. 總結: 如果傳回一個普通值,除了promise ,就傳遞給下一個then的成功,如果傳回一個失敗的promise或者抛出異常(try catch),會走下一個then的失敗
myPromise.js
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) { // 防止自己等待自己 一直循環等待
// 原生傳回:return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
return reject(new TypeError('我們自己報的錯:循環引用報錯'))
}
let called; // 為了相容其他promise 庫符合a+規範,防止狀态改變後再次被調用
// 考慮其他promise庫相容問題:判斷x是不是promise:不是null的對象 || 函數
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try { // 為防止then時 出現異常,Object.defineProperty
let then = x.then // 取x 的then 方法
/**
* 可能對象是{then:{}},還需要判斷then是否是函數,
* 是函數就認為是promise,并調用then方法
*/
if (typeof then === 'function') {
// call第一個參數是this,後面的是成功回調 和 失敗回調
// 如果成功回調傳回值 y 仍然是promise,就繼續遞歸解析promise,解析到傳回值不是一個Promise類型為止
// promise2能否成功,是根據x的值來定的,x是promise,那麼就要等到x完成,x完成後,如果傳回y又是promise,那promise2又要等到y完成
then.call(x, y =>{
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, e => {
if (called) return
called = true
reject(e)
})
} else {
// 否則就認為 then 是一個普通對象,成功即可
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e) // 出現異常,直接走失敗邏輯
}
} else { // x 為普通值
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onfulfilledCallbacks = []
this.onrejectedCallbacks = []
let resolve = (value) => {
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onfulfilledCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onrejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onfulfilled, onrejected) {
// 要點 12: then鍊式調用,傳回的并不是this,而是一個`新的promise執行個體`
let promise2 = new Promise((resolve, reject) => { // new Promise執行器中的代碼是立即執行,是以沒有影響
if (this.status === RESOLVED) {
// 加定時器是因為在目前執行上下文中,不能擷取promise2的值
setTimeout(() => {
// 要點13.2. 用于報錯或抛出異常處理,以下同理
try {
// 要點13:存儲第一次then的回調結果傳回值,用于下次then的參數傳遞,以下let x 同理
let x = onfulfilled(this.value)
// 要點13.3: x 可能會是一個promise, 需要單獨處理,
// 并将promise2、新的傳回值:x 、成功回調resolve 失敗回調reject 作為參數傳遞
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
// 要點13.2. 報錯或抛出異常處理,直接走promise2的reject,以下同理
reject(e)
}
})
}
// 以下邏輯同理
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === PENDING) {
// 暫存成功回調隊列
this.onfulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
// 暫存失敗回調隊列
this.onrejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2 // 要點 12: 傳回一個新的promise執行個體
}
}
// Commonjs規範導出子產品
module.exports = Promise
myPromise — 值的透傳
Promise 值的穿透,當一個promise連續then多次 ,并且then中沒有傳回任何值時,此時data會穿透值最後一個then中test.js。
//原生方法:值的穿透 示範
let p = new Promise((resolve,reject)=>{
resolve(1)
// reject(2) // 同理
})
p.then().then().then(data=>{
console.log(data); // 1
})
p.then().then().then(null,err=>{
console.log(err); // 2
})
// ----------------------------------------------------------
//myPromise:基于以上幾部分的實作,我們自己的promise是不支援穿透的,那麼看一下演變過程,就很容易寫出符合規範的透傳
const Promise = require('./1.myPromise')
let p1 = new Promise((resolve,reject)=>{
resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
reject(2) // 同理,
})
// 相當于, 啥都不寫時,感覺像是預設定義了一個函數,不停向下傳遞,去實作一下吧~
p1.then(data => data)
.then(data => data)
.then(data =>{
console.log(data);
})
// 相當于, throw err,不停向下傳遞
p2.then(null,err=>{
throw err
}).then(null,err=>{
console.log(err);
throw err
}).then(null,err=>{
console.log(err);
})
myPromise.js
上面重複的代碼是在是太多了,就不在贅述了,怕看着更暈了,來, 找到類中then方法定義的位置,加上下面兩行代碼,就可以完美實作成功及失敗的資料透傳。
...
...
...
// 在這裡這裡...其他都不變
then(onfulfilled, onrejected) {
// onfulfilled 是函數就執行,不是函數包一層函數并直接傳回資料
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : v => v;
// onrejected 是函數就執行,不是函數包一層函數并直接抛出錯誤
onrejected = typeof onrejected === 'function' ? onrejected : err => { throw err };
...
...
...
完美通過官方872條用例
為了測試myPromise庫,必須公開一個非常小的擴充卡接口。可以說是promise的延遲對象defer,下文會詳細介紹,各位稍安勿躁。
- 第一步:找到檔案最下面。加上下面幾行代碼,
/**
* ...
* 其餘不變
* ...
*/
// 測試自己的寫的promise 是否符合a+規範
// promise的延遲對象
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject
})
return dfd;
}
module.exports = Promise
第二步:安裝測試包 npm install promises-aplus-tests -g
第三步:進入對應目錄執行 promises-aplus-tests ./1.myPromise.js
如果你的測試結果和我圖中标注一樣,那麼恭喜你,如果驗證不通過,肯定是有一些不一樣,注意看報錯資訊,修正一下就好了。
myPromise的延遲對象defer用法
promise是為了解決異步回調函數嵌套問題,那麼可以通過promise的延遲對象來減少一層嵌套關系
模拟資料準備,準備以下兩個檔案,
- name.txt:檔案内容是:age.txt
- age.txt:檔案内容18
需求:首先通過fs子產品讀取到name中的内容(讀到新的filepath),再去讀取age.txt中的内容并輸出。
沒有延遲對象的寫法:
const fs = require('fs')
const Promise = require('./1.myPromise')
function read(filepath){
// 想要read方法可以使用then方法,需要包一層new Promise
return new Promise((resolve,reject)=>{
fs.readFile(filepath,'utf8',function(err,data){
if(err) return reject(err)
resolve(data)
})
})
}
read('./name.txt').then(data=>{
return read(data) // data = age.txt
}).then(data=>{
console.log(data); // data = age.txt
})
使用延遲對象的寫法:
const fs = require('fs')
const Promise = require('./1.myPromise')
function readDefer(filepath){
let dfd = Promise.defer() // 減少一層嵌套
fs.readFile(filepath,'utf8',function(err,data){
if(err) return dfd.reject(err)
dfd.resolve(data)
})
return dfd.promise
}
readDefer('./name.txt').then(data=>{
return readDefer(data) // data = age.txt
}).then(data=>{
console.log(data); // data = age.txt
})
myPromise.catch
完成以上内容,就已經實作了Promise A+ 規範的全部内容,而我們常用的catch及finally并不屬于 A+規範,同樣可以實作一下,catch是屬于執行個體上的方法,用于出錯處理及捕獲異常
演變過程:test.js
const Promise = require('./1.myPromise')
new Promise((resolve, reject) => {
reject('失敗')
}).then(data => { // 第一個then沒有錯誤處理方法,直接會傳遞到第二個then中
console.log(data);
}).then(null, err => {
console.log(err, 'errrrrr'); // 失敗 errrrrr
})
// 将上述代碼中null優化一下,于是就有了catch方法
new Promise((resolve, reject) => {
reject('失敗')
}).then(data => {
console.log(data);
}).catch(err => {
console.log(err, 'errrrrr'); // 失敗 errrrrr
})
實作:myPromise.js
class Promise{
// 重複代碼省略
// constructor(){...}
// then(){...}
catch(onrejected){ // 執行個體的catch
return this.then(null,onrejected)
}
}
/**
* 其餘重複代碼省略
*/
myPromise.finally
finally方法是屬于執行個體上的方法.表示無論成功還是失敗,都會執行,并且可以向下執行。
了解起來有點點繞哈,建議多看幾遍吧,test.js
const Promise = require('./1.myPromise')
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('成功') // 1
reject('失敗') // 2
}, 1000)
}).finally(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
// resolve(1000) // 3 不會采用promise成功的結果,但是會等待執行
reject(9000) // 4 但是傳回一個失敗的promise,會走catch,相當于throw new Error
}, 1000)
})
}).then(data => {
console.log(data); // 對應上述不同序号組合,輸出結果有所不同,可自行組合實驗,下方也會給大家作出總結
}).catch(e => {
console.log(e, 'catch');
})
/**
* 整理流程:
* 組合:
* 1+3:成功 + 成功 ,走then=>data 傳回 1 傳回的資料('成功')
* 1+4:成功 + 失敗 ,走then=>err 抛出 4 傳回的資料(9000)
* 2+3:失敗 + 成功 ,走then=>err 抛出 2 傳回的資料('失敗')
* 2+4:失敗 + 失敗 ,走then=>err 抛出 4 傳回的資料(9000)
*/
實作:myPromise.js
/**
* 1. finally 傳遞了一個方法 callback
* 2. finally 方法相當于一個then,特點就是在函數中傳回一個promise,并會等待這個promise的完成
* 3. Promise.resolve 又會等待 callback執行完成
*/
class Promise{
// 重複代碼省略
// constructor(){...}
// then(){...}
// catch(){...}
finally(cb){
return this.then((data)=>{ // 成功 || 失敗 都會執行
// Promise.resolve 目的是等待cb() 後的promise完成
// 成功時調用finally,直接傳遞this執行個體成功的資料data,finally方法本身是沒有參數的
return Promise.resolve(cb()).then(()=>data)
},(err)=>{
// 失敗時調用finally,等待Promise.resolve(cb())的執行的結果,當cb結果為失敗時,直接以失敗的結果直接走catch
// 而隻有在Promise.resolve成功時候,才會去執行.then(()=>{throw err}),抛出this執行個體reject的err資訊
return Promise.resolve(cb()).then(()=>{throw err})
})
}
}
/**
* 其餘重複代碼省略
*/
myPromise.resolve
Promise.resolve() 方法會創造一個成功的promsie,屬于類上的方法,通過className調用
演變過程:test.js
const Promise = require('./1.myPromise')
Promise.resolve(200).then(data => {
console.log(data); // 200
})
// 等價于
new Promise((resolve, reject) => {
resolve(200)
}).then(data => {
console.log(data); // 200
})
實作:myPromise.js
constructor(executor) {
/**
* 其餘重複無改動
*/
let resolve = (value) => {
// 一個promise直接resolve 另一個promise時,會等待裡面的promise執行完,傳回成功的執行結果
if (value instanceof Promise) {
return value.then(resolve, reject)
}
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onfulfilledCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
// reject 一個promise時,不需要單獨處理,失敗就直接走失敗的邏輯,不用處理其傳回值
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onrejectedCallbacks.forEach(fn => fn())
}
}
/**
* 其餘重複無改動
*/
}
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
myPromise.reject
Promise.reject() 方法會創造一個失敗的promsie,屬于類上的方法,通過className調用。
演變過程:test.js
const Promise = require('./1.myPromise')
Promise.reject(100).then(null,err => {
console.log(err, 'errrrrr'); // 100 errrrrr
})
// 等價于
new Promise((resolve, reject) => {
reject(100)
}).then(data=>{
console.log(data);
},err => {
console.log(err, 'errrrrr'); // 100 errrrrr
})
實作:myPromise.js
class Promise{
/**
* 其餘重複無改動
*/
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
}
/**
* 其餘重複無改動
*/
myPromise.all
Promise.all()方法用于将多個 Promise 執行個體,包裝并傳回一個新的 Promise 執行個體p
p的狀态由傳遞的多個 Promise 決定,分成兩種情況。(官網是這樣說的)
(1)隻有所有promise執行個體的狀态都變成fulfilled,p的狀态才會變成fulfilled,此時數組中promise的傳回值組成一個數組,傳遞給p的回調函數。
(2)隻要數組的中promise有一個被rejected,p的狀态就變成rejected,此時第一個被reject的執行個體的傳回值,會傳遞給p的回調函數。
test.js
const Promise = require('./1.myPromise')
Promise.all([
1, 2, 3,
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 1000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功') // 情況1
// reject('失敗') // 情況2
}, 1000)})
]).then(data => {
console.log(data); // 情況1. [ 1, 2, 3, '成功', '成功' ]
}).catch(err => {
console.log(err); // 情況2. 失敗
})
myPromise.js
class Promise{
// 在原有代碼基礎上,添加一個靜态方法
static all(promises){
return new Promise((resolve,reject)=>{
let result = []
let times = 0
const processSuccess = (index,val)=>{
result[index] = val
if(++times === promises.length){
resolve(result)
}
}
for (let i = 0; i < promises.length; i++) { // 并發。多個請求一起執行
let p = promises[i];
if(p && typeof p.then === 'function'){ // 判斷p是不是promise執行個體,是則繼續等待p的執行結果,處理p對應的成功和失敗
p.then(data=>{
processSuccess(i,data)
},reject) // 如果其中一個promise失敗了,直接執行失敗回調,由于reject本身就是函數,故可簡寫
}else{ // 如果不是promise執行個體就直接調用成功,并存儲該值
processSuccess(i,p)
}
}
})
}
}
myPromise.race
Promise.race()方法同樣是将多個 Promise 執行個體,包裝并傳回一個新的 Promise 執行個體p。
隻要有一個執行個體率先改變狀态,p的狀态就跟着改變。那個率先改變的 Promise 執行個體的傳回值,就傳遞給p的回調函數。
(簡單了解:race 賽跑,采用最快的那一個,race方法如果其中一個完成了,其他方法還是會執行完,隻是并沒有采用他的結果)
test.js
const Promise = require('./1.myPromise')
Promise.race([
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功')
}, 2000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
reject('失敗')
}, 1000)
})
]).then(data => {
console.log(data);
}).catch(err => {
console.log(err); // 輸出:失敗,resolve 2s執行,reject 1s執行,reject更快
})
myPromise.js
class Promise{
static race(promises){
return new Promise((resolve,reject)=>{
// for循環執行,隻要有一個狀态發生改變,就直接将該執行個體的傳回值 傳回
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
if(p && typeof p.then === 'function'){
p.then(data=>{
resolve(data)
},reject)
}else{
resolve(p)
}
}
})
}
}
myPromise終極版本
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
const resolvePromise = (promise2, x, resolve, reject) => {
if (promise2 === x) {
return reject(new TypeError('循環引用報錯'))
}
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let called;
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, e => {
if (called) return
called = true
reject(e)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onfulfilledCallbacks = []
this.onrejectedCallbacks = []
let resolve = (value) => {
if (value instanceof Promise) {
return value.then(resolve, reject)
}
if (this.status === PENDING) {
this.value = value
this.status = RESOLVED
this.onfulfilledCallbacks.forEach(fn => fn())
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onrejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onfulfilled, onrejected) {
onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : v => v;
onrejected = typeof onrejected === 'function' ? onrejected : err => {
throw err
};
let promise2 = new Promise((resolve, reject) => {
if (this.status === RESOLVED) {
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === PENDING) {
this.onfulfilledCallbacks.push(() => {
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
this.onrejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
})
return promise2
}
catch (onrejected) {
return this.then(null, onrejected)
}
finally(cb) {
return this.then(
(data) => {
return Promise.resolve(cb()).then(() => data)
}, (err) => {
return Promise.resolve(cb()).then(() => { throw err })
})
}
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
static all(promises) {
return new Promise((resolve, reject) => {
let result = []
let times = 0
const processSuccess = (index, val) => {
result[index] = val
if (++times === promises.length) {
resolve(result)
}
}
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
if (p && typeof p.then === 'function') {
p.then(data => {
processSuccess(i, data)
}, reject)
} else {
processSuccess(i, p)
}
}
})
}
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
let p = promises[i];
if (p && typeof p.then === 'function') {
p.then(data => {
resolve(data)
}, reject)
} else {
resolve(p)
}
}
})
}
}
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject
})
return dfd;
}
module.exports = Promise