前言:promise的js實現網上有很多,但基本都是基于es5的,es6有著更簡介的語法,為什么不嘗試一下呢?
階段一:
只支持鏈式調用不支持其他api
const pending = 'pending'
const resolved = 'resolved'
const rejected = 'rejected'
class MyPromise {
constructor (cb) {
this._status = pending // 初始化狀態
this._data = null // 成功傳遞的消息
this._error = null // 失敗傳遞的消息
this.successSubs = [] // resolve后需要執行的回調隊列
this.failSubs = [] // reject后需要執行的回調隊列
if (typeof cb === 'function') {
cb(this.resolve.bind(this), this.reject.bind(this)) // 綁定當前實例
} else if(cb && typeof cb !== 'function') {
return new TypeError('MyPromise constructor must be a function')
}
}
resolve (_data) {
// 這里應該有兩種情況
// 1 調用then方法后訂閱了
// 2 直接調用Promise.resolve()方法
// 情況1:
if (this._status === pending) {
this._status = resolved
this._data = _data
this.successSubs.forEach(fn => fn())
}
}
reject (_error) {
if (this._status === pending) {
this._status = rejected
this._error = _error
this.failSubs.forEach(fn => fn())
}
}
resolvePromise (x,resolve, reject) {
// 如果返回的是MyPromise實例
if (x instanceof MyPromise) {
x.then(data => {
resolve(data)
},
error => {
reject(error)
})
} else {
// 普通值:直接執行then返回的新promise方法的resolve,后一個then屬于這個實例的,訂閱隊列也是屬于這個實例的
resolve(x)
}
}
then (success, fail) {
// 鏈式調用 后一個then調用前一個then返回的promise實例
let p = new MyPromise((resolve, reject) => {
if (this._status === pending) {
// promise實例還在pending狀態調用了then方法,增加訂閱者
if (success && typeof success === 'function') {
this.successSubs.push(() => {
try {
// 用戶調用then方法,callback函數,兩種情況
// 1 非promise對象: return 什么就作為參數傳遞給下個then什么
// 2 promise對象:擇要等用戶的promise的有結果才執行下一個函數
let x = success(this._data)
// 統一封裝到一個函數中處理
this.resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (fail && typeof fail === 'function') {
this.failSubs.push(() => {
try {
let x = fail(this._error)
this.resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
} else if (this._status === resolved) {
try {
let x = success(this._data)
this.resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
} else if(this._status === rejected) {
try {
let x = fail(this._error)
this.resolvePromise(x, resolve, reject)
} catch (e) {
reject(e)
}
}
})
return p
}
}
階段二:
實現其他api:
Promise.resolve()和Promise.reject()
這兩個方法都返回一個非pending狀態的promise對象,同時Promise類才有的方法,實例沒有該方法。和es6中class static方法完美契合。
...
// 直接調用了Promise.resolve方法
static resolve (value) {
return new MyPromise(resolve => {
resolve(value)
})
}
// 直接調用了Promise.reject
static reject(reason) {
return new MyPromise((undefined, reject) => {
reject(reason)
})
}
...
Promise.all
Promise.all(iterable): 方法返回一個
Promise
實例,此實例在iterable
參數內所有的promise
都“完成(resolved)”或參數中不包含promise
時回調完成(resolve);如果參數中promise
有一個失敗(rejected),此實例回調失敗(reject),失敗原因的是第一個失敗promise
的結果。
整理思路:
1 判斷一個值是否可迭代,可以用Symbol.iterator屬性判斷,直接用for...of迭代
2 如果全部正常(沒有reject)則可以將該實例resolve。
3 調用resolve的時機,因為是異步的,不曉得所有的iterable啥時候執行完,所以每次push的時候判斷一下是否可以結束,可以結束就resolve。
4 reject則只需要rejecte當前失敗的reason,前面的全部丟棄
5 static私有方法符合需求
static all (iterable) {
if (iterable[Symbol.iterator]) {
return new MyPromise((resolve, reject) => {
let resolveArr = []
let len = iterable.length
// 檢查是不是所有的promise都完成了
function checkAll () {
if (resolveArr.length === len) {
resolve(resolveArr)
}
}
try {
for (let x of iterable) {
// 每項可以是Promise值和其他值
if (x instanceof MyPromise) {
x.then(data => {
resolveArr.push(data)
checkAll()
}, reason => {
reject(reason)
})
} else {
resolveArr.push(x)
checkAll()
}
}
} catch (e) {
reject(e)
}
})
} else {
// 不是可迭代對象:拋出一個錯誤
let str = ({}).toString.call(iterable)
let reg = /^\[object\s([A-Z][a-z]{2,8})\]$/
let matchArr = str.match(reg)
let msg = (matchArr && matchArr[1]) || str
throw new TypeError( msg + ': is not iterable')
}
}
測試mdn的例子:
結果返回:
MyPromise
與mdn的一致
原生Promise
再看一個例子:
image.png
原生的promise返回結果順序和傳入的一致,我們實現的是誰先resolve就誰先push,顯然還需要保證順序。
腦海里冒出如下方案:
1 for循環或者forEach提供了參數index, 和值value方便我們直接賦值,但是Set和Map不支持,而且我們結束的判斷resolveArr.length === iterable.length有可能會碰到坑。
2 提供一個key,用于標記順序
目前就想到兩個方案,只能選擇方案2:
static all (iterable) {
if (iterable[Symbol.iterator]) {
return new MyPromise((resolve, reject) => {
let resolveArr = []
let len = iterable.length
// 檢查是不是所有的promise都完成了
function checkAll () {
if (resolveArr.length === len) {
resolve(resolveArr.sort((a, b) => {
return a.key - b.key
}).map(v => v.data))
}
}
try {
let key = -1
for (let x of iterable) {
key++
// 每項可以是Promise值和其他值
if (x instanceof MyPromise) {
x.then(data => {
resolveArr.push({data, key})
checkAll()
}, reason => {
reject(reason)
})
} else {
resolveArr.push({data:x, key})
checkAll()
}
}
} catch (e) {
reject(e)
}
})
} else {
// 不是可迭代對象:拋出一個錯誤
let str = ({}).toString.call(iterable)
let reg = /^\[object\s([A-Z][a-z]{2,8})\]$/
let matchArr = str.match(reg)
let msg = (matchArr && matchArr[1]) || str
throw new TypeError( msg + ': is not iterable')
}
}
檢測一下:
image.png
Promise.race
Promise.race(iterable) 方法返回一個 promise,一旦迭代器中的某個promise解決或拒絕,返回的 promise就會解決或拒絕。
race方法與all方法類似,但是相對來說更簡單:
static race (iterable) {
if (iterable[Symbol.iterator]) {
return new MyPromise((resolve, reject) => {
try {
for (let x of iterable) {
// 每項可以是Promise值和其他值
if (x instanceof MyPromise) {
x.then(data => {
resolve(data)
}, reason => {
reject(reason)
})
} else {
resolve(x)
}
}
} catch (e) {
reject(e)
}
})
} else {
// 不是可迭代對象:拋出一個錯誤
let str = ({}).toString.call(iterable)
let reg = /^\[object\s([A-Z][a-z]{2,8})\]$/
let matchArr = str.match(reg)
let msg = (matchArr && matchArr[1]) || str
throw new TypeError( msg + ': is not iterable')
}
}
測試mdn例子
image.png