前言: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的例子:
结果返回:
与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
网友评论