本文主要通过例举promise的用法,来实现一个promise。
状态初始化
promise用法
let p = new Promise((res, rej) => {
// res('success')
rej('err')
})
console.log(p1);
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "success"
promise初始化实现
首先,确定promise用一个类可以实现,promise初始化时,需要知道promise的状态,以及promise的返回值。
在promise中,存在PromiseState、PromiseResult两属性,分别存放promise的状态和结果。
用户在new Promise时,传入的是一个回调函数,在函数中使用resolve、rejected。
- 在调用resolve时,将状态修改为fulfilled,并给结果赋值。
- 在调用reject时,将状态修改为rejected,并给结果赋值。
初始化如下:
class MyPromise {
constructor(cb) {
this['[[PromiseState]]'] = 'pending'
this['[[PromiseResult]]'] = undefined
this.onResolved = [] // 成功回调集合 两个集合暂未使用
this.onRejected = [] // 存放失败的回调集合
cb(this.#resolve.bind(this), this.#reject.bind(this)) // 需要绑定this,否则获取不到
}
#resolve(val) {
this['[[PromiseState]]'] = 'fulfilled'
this['[[PromiseResult]]'] = val
}
#reject(val) {
this['[[PromiseState]]'] = 'rejected'
this['[[PromiseResult]]'] = val
}
}
如果在cb调用不对promise内部私有属性绑定this,运行后就会报错:
this输出为undefined,此时获取不到this,所以需要在cb调用时,绑定this。
静态方法
resolve
Promise.resolve() 返回一个解析过的promise对象。
使用方法
console.log(Promise.resolve('success'));
// Promise{
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "success"
// }
实现
直接使用resolve静态方法,输出的结果等同于
new Promise((res, rej) => { res('success') })。因此,在调用resolve方法时,需要实例化一个Promise类,在内部调用res。
static resolve(val) {
return new MyPromise((res, rej) => {
res(val)
})
}
reject
Promise.reject() 返回一个拒绝的Promise对象。
使用方法
console.log(Promise.reject('err'));
// Promise{
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: "err"
// }
实现
直接使用resolve静态方法,输出的结果等同于
new Promise((res, rej) => { rej('err') })。
static reject(val) {
return new MyPromise((res, rej) => {
rej(val)
})
}
allSettled
Promise.allSettled() 接收一个由已经执行完promise(状态为:fulfilled或rejected)的结果组成的对象数组。
allSettled用法
let p2 = new Promise((res, rej) => {
setTimeout(() => {
res("p1");
}, 2000);
})
let p3 = new Promise((res, rej) => {
setTimeout(() => {
rej("p2 err");
}, 1000);
})
Promise.allSettled([p2, p3]).then(res => {
console.log(res); // [{status: "fulfilled", value: "p1"},{status: "rejected", reason: "p2 err"}]
})
实现方法
allSettled接收的是一个对象数组,不管状态如何,结果输出的也是一个数组。在数组循环调用then时,需要考虑两种状态的返回结果,在不同的状态中,分别设置该项中的数据返回格式。例如:{status: "fulfilled", value: "p1"},成功中两个属性分别为status和value。
在实现时,需要设置一个变量,存储返回新结果的长度num。如果num等于接收数组lists长度,则表明已经全部修改统一格式,此时就需要执行下一步回调。
static allSettled(lists) {
let resArr = new Array(lists.length)
let num = 0
return new MyPromise((resolve) => {
lists.forEach((item, key) => {
let obj = {}
item.then(
(res) => {
obj['status'] = 'fulfilled'
obj['value'] = res
resArr[key] = obj
num++
if (num >= lists.length) {
resolve(resArr)
}
},
(err) => {
obj['status'] = 'rejected'
obj['reason'] = err
resArr[key] = obj
num++
if (num >= lists.length) {
resolve(resArr)
}
},
)
})
})
}
all
Promise.all 等待所有的都完成,如果all中数组有一个错误,则执行异常。
all用法
let p2 = new Promise((res, rej) => {
setTimeout(() => {
res("p1");
}, 2000);
})
let p3 = new Promise((res, rej) => {
setTimeout(() => {
res("p2");
}, 1000);
})
Promise.all([p2, p3])
.then(res => {
console.log(res);
})
// ["p1", "p2"]
all实现
和allSettled类似,只需要考虑成功的情况,但是还是需要判断所有的是否都已执行完成。
static all(list) {
return new Promise((resolve, reject) => {
if (!Array.isArray(list)) {
return reject(new Error('传入的必须是数组'))
}
let res = [],
l = list.length,
count = 0
list.forEach((_, i) => {
Promise.resolve(_)
.then((result) => {
count++
res[i] = _
if (count === l) resolve(res)
})
.catch((e) => reject(e))
})
})
}
但是真实的all:是所有回调都完成返回,如果其中一个异常,其他的回调也会执行,不过最终返回的状态是reject。
race
Promise.race()谁执行的时间短就执行谁,无论状态如何。
race用法
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.race([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 打开的是 'failed'
})
race实现
在遍历lists时,直接调用then。
static race(lists) {
return new MyPromise((resolve, reject) => {
lists.forEach((item) => {
item.then(
(res) => {
resolve(res)
},
(err) => {
reject(err)
},
)
})
})
}
any
Promise.any()返回成功状态中的第一个。
any用法
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.any([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})
// success
any实现
执行成功状态的第一个回调。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise 和AggregateError类型的实例。
static any(lists) {
return new MyPromise((resolve, reject) => {
let errs = []
let len = lists.length
if (len === 0) reject(new AggregateError('All promises were rejected'))
lists.forEach(item => {
item.then(res => {
resolve(res)
}, err => {
len--
errs.push(err)
if (len === 0) reject(new AggregateError(errs))
})
})
})
}
原型方法
then原型方法
Promise.prototype.then() 最多接收两个回调函数:成功和失败情况下的回调函数。
基本then
基本then用法
let p = new Promise((res, rej) => {
res('success');
})
p.then(res => {
console.log(res); // success
}, err => {
console.log(err);
})
基本then的实现
通过上段then的基本用法,可以根据new Promise内执行的res或rej,then中分别对应正确的回调或抛出错误。在then中就可以判断promise的状态来执行不同的回调。
then(onFullfill, onReject) {
if (this['[[PromiseState]]'] === 'fulfilled') {
onFullfill(this['[[PromiseResult]]'])
}
if (this['[[PromiseState]]'] === 'rejected') {
onReject(this['[[PromiseResult]]'])
}
}
状态为fulfilled时,表示成功;为rejected,表示失败。 调用多个then时,根据promise中调用的释放回调用,同时多个then中相应的回调也会触发。
let p = new MyPromise((res, rej) => {
res('success')
});
p.then(res => {
console.log(res);
}, err => {
console.log('err', err);
})
p.then((res) => {
console.log(res, '2');
}, (rej) => {
console.log('err2');
})
// success
// success 2
then的链式调用
then的链式用法
let p1 = new Promise((res, rej) => {
res('success');
})
p1.then(res => {
return new Promise((res, rej) => {
res('success2')
})
}, err => {
console.log(err);
}).then(res => {
console.log('2', res); // 2 success2
})
then的链式实现
链式的实现,就相当于返回一个promise。根据上一个promise的状态,在then中执行相应状态的回调,依次类推。如果上一个then中返回一个promise,则此时then中的状态为'pending'。
#resolve(val) {
if (this['[[PromiseState]]'] !== 'pending') return
setTimeout(() => {
this['[[PromiseState]]'] = 'fulfilled'
this['[[PromiseResult]]'] = val
// this.onResolvedQueue.forEach((fn) => fn && fn(val))
let cb;
while(cb = this.onResolvedQueue.shift()) {
cb(val)
}
})
}
#reject(err) {
if (this['[[PromiseState]]'] !== 'pending') return
setTimeout(() => {
this['[[PromiseState]]'] = 'rejected'
this['[[PromiseResult]]'] = err
// this.onRejectedQueue.forEach((fn) => fn && fn(err))
let cb;
while(cb = this.onRejectedQueue.shift()) {
cb(err)
}
})
}
then(onFullfill, onReject) {
// 返回的是新promise
return new MyPromise((resolve, reject) => {
// 成功执行的函数
let onFullfillFn = (val) => {
let res = onFullfill && onFullfill(val)
// 返回一个新的promise 执行下一个回调
res instanceof MyPromise ? res.then(resolve) : resolve(res)
}
// 失败执行的函数
let rejectFn = (err) => {
onReject && onReject(err)
reject(err)
}
this.onResolvedQueue.push(onFullfillFn)
this.onRejectedQueue.push(rejectFn)
})
}
then中会接受最多两个的回调函数,在成功的回调中,传入的可能直接是逻辑处理,也可能是需要返回一个新的promise对象,所以在返回时,需要判断返回类型。如果传入的是失败函数,直接执行用户失败的回调。
catch原型方法
Promise.prototype.catch() 捕获rejected情况,并处理。
catch用法
let p = new Promise((res, rej) => {
rej('err')
})
p.catch((reason) => {
console.log(reason) // err
})
catch实现
直接执行then的失败回调。
catch(cb) {
this.then(undefined, cb)
}
finally原型方法
Promise.prototype.finally() 无论是fulfiled还是rejected,都会执行finally内的回调函数。
finally用法
let p = new Promise((res, rej) => {
res('success')
})
p.then((reason) => {
console.log(reason)
}).finally(() => {
console.log('finally');
})
// success finally
finally实现
finally(cb) {
return this.then(cb, cb)
}