【磨破嘴皮】手写完整功能Promise
Promise API
要写一个Promise, 需要简单熟悉下Promsie的Api,与Promise的状态
状态
对于表示一个异步操作的最终完成或失败以及其他结果,共有三个状态
- pending 初始状态、既没有被兑现也没有拒绝
- fulfilled 操作成功完成
- rejected 操作失败
Api
- all 接收一个Iterable类,返回一个promise实例,所有的promise完成,或某一个promise 被拒绝,都会立即抛出错误
- allSettled 接收一个Iterable类、只有当传入的所有promise,都敲定(rejected, fullfilled)才将返回的promise兑现,并返回所有promise的结果对象数组
- any 接收一个Iterable类,只要可迭代对象中的某一个promise被兑现(fullfilled)就会立即兑现返回的promise,仅当可迭代对象中所有的Promise都被拒绝(rejected),返回的promise对象才会修改为rejected
- race 接收一个Iterable类,只要可迭代对象中其中一个被兑现(fullfilled)或者被拒绝(rejected)返回的promise就会立即被兑现或拒绝
- reject 传入拒绝原因、立即返回一个被拒绝状态的promise对象
- resolve 传入兑现结果,立即返回一个被兑现的promise对象,如果传入的是thanable则会采用thenalbe的最终状态
如何在resolve执行时,调用then方法
这里利用了一个简单的发布订阅模式,发布了成功失败两个事件因此:.then传入的两个参数可以理解为成功与失败的订阅,resolve,rejcet则是成功或失败事件的派发
.then其实是将传入的回调存储在数组中,待resolve调用后,遍历数组执行
.then
MyPromise.prototype.then = function (onFulfilled, onRejected) {
if (this.status === PENDING) {
// 成功的回调
this.onFulfilledCallbacks.push(realOnFulfilled);
// 失败的回调
this.onRejectedCallbacks.push(realOnRejected);
}
}
Resolve 与reject方法遍历执行.then的回调
function resolve(value) {
if (that.status === PENDING) {
that.status = FULFILLED;
that.value = value
that.onFulfilledCallbacks.forEach((callback) => {
callback(that.value)
})
}
}
function reject(reason) {
if (that.status === PENDING) {
that.status = REJECTED
that.reason = reason
that.onRejectedCallbacks.forEach((callback) => {
callback(that.reason)
})
}
}
如何让.then方法的回调异步执行
onFulfilled
和 onRejected
方法异步执行,且应该在 then
方法被调用的那一轮事件循环之后的新执行栈中执行。所以在我们执行onFulfilled
和 onRejected
的时候都应该包到setTimeout
里面去。
function timeoutWrapper(fn) {
return function (...args) {
const time = setTimeout(()=>{
fn(...args)
clearTimeout(time)
}, 0)
}
}
_fullfille = timeoutWrapper(()=>{
try {
// 如果传入的onFulfilled是一个值,则需要将这个值传递给下一个.then
if (typeof onFulfilled !== 'function') {
_resolve(this.value)
} else {
_returnVal = realOnFulfilled(this.value)
_resolve(this.value)
}
} catch (e) {
_reject(e)
}
})
如何获取回调返回的值,并兼容thenable
.then的回调中,返回的值会传递给下一个.then,执行then回调时,使用_returnVal接收它返回的值,并按值的类型调用resolvePromise方法,决定不同的处理方式
- 值为promise则执行传入的promise.then并递归执行resolvePromise,处理这个promise中.then会调的返回值
- 当返回值为对象时,则要考虑thenable的情况,如果只是普通对象则作传递给下一个.then的回调作为入参
// 将传入的方法包裹在 timeout中执行
MyPromise.prototype.then = function (onFulfilled, onRejected) {
let realOnFulfilled = onFulfilled
const that = this
if (typeof realOnFulfilled !== 'function') {
realOnFulfilled = function (value) {
return value
}
}
let realOnRejected = onRejected
if (typeof realOnRejected !== 'function') {
realOnRejected = function (reason) {
if (reason instanceof Error) {
throw reason;
} else {
throw new Error(reason)
}
}
}
// 失败
let _reject;
// 成功
let _resolve;
// 返回值
let _returnVal;
// 按照promise的状态,执行对应的兑现方法
let _fullfill;
const _promise = new MyPromise(function (resolve, reject) {
_reject = reject;
_resolve = resolve
})
// 如果一个promise已经被兑现
if (this.status === FULFILLED) {
_fullfill = timeoutWrapper(()=>{
try {
// 如果传入的onFulfilled是一个值,则需要将这个值传递给下一个.then
if (typeof onFulfilled !== 'function') {
_resolve(this.value)
} else {
_returnVal = realOnFulfilled(this.value)
_resolve(this.value)
}
} catch (e) {
_reject(e)
}
})
}
// 如果promise已经被拒绝
if (this.status === REJECTED) {
_fullfill = timeoutWrapper(()=>{
try {
if (typeof onRejected !== 'function') {
_reject(this.reason)
} else {
_returnVal = realOnRejected(this.reason)
_resolve()
}
} catch (e) {
_reject(e)
}
})
}
// 如果promise还在等待中,则将回调暂存到数组中
if (this.status === PENDING) {
this.onFulfilledCallbacks.push(timeoutWrapper(function () {
try {
_returnVal = realOnFulfilled(that.value)
} catch (e) {
_reject(e)
}
}));
this.onRejectedCallbacks.push(timeoutWrapper(function () {
try {
_returnVal = realOnRejected(that.reason)
} catch (e) {
_reject(e)
}
}));
}
// 当promise已经被兑现时,直接执行回调
_fullfill && _fullfill()
// 处理回调返回的值
resolvePromise(_promise, _returnVal, _resolve, _reject)
return _promise
}
function resolvePromise(promise, x, resolve, reject) {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
// 这是为了防止死循环
if (promise === x) {
return reject(new TypeError('The promise and the return value are the same'));
}
if (x instanceof MyPromise) {
x.then(function (y) {
resolvePromise(promise, y, resolve, reject)
}, reject)
} else if (typeof x === 'object' || typeof x === 'function') {
if (x === null) {
return resolve(x);
}
let then;
let called = false;
// thenable 兼容
try {
then = x.then;
} catch (e) {
return reject(error);
}
if (typeof then === 'function') {
try {
then.call(x, function (y) {
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
}, function (r) {
if(called) return;
called = true
reject(r)
})
} catch(e) {
if(called) return;
reject(e)
}
} else {
resolve(x)
}
}
}
.catch实现
catch将在promise被reject或方法执行出错时执行
MyPromise.prototype.catch = function (onRejected) {
this.then(null, onRejected)
}
.finally实现
无论回调兑现或拒绝,始终执行一次
MyPromise.prototype.finally = function (callback) {
return this.then(function (val) {
return MyPromise.resolve(callback()).then(function () {
return val
})
}, function() {
return MyPromise.resolve(callback()).then(function () {
throw error
});
})
}
Promise.resolve实现
resolve接收兑现的值,并直接返回一个被兑现的promise
MyPromise.resolve = function (parameter) {
if(parameter instanceof MyPromise) {
return parameter
}
return new MyPromise((resolve)=>{
resolve(parameter)
})
}
Promise.reject实现
MyPromise.reject = function (reason) {
return new MyPromise((_, reject)=>{
reject(reason)
})
}
Promise.all实现
接收一个可迭代的promise集合,或数组,当所有的promise被兑现,或任意一个promise被拒绝时立即兑现返回的promise
MyPromise.all = function(promiseList) {
const resPromise = new MyPromise(function (resolve, reject) {
let count = 0
let result = []
let length = promiseList.length
if(length === 0) {
return resolve()
}
promiseList.forEach(function(promise, index) {
MyPromise.resolve(promise).then(function(value) {
count++;
result[index] = value;
if(count === length) {
resolve(result)
}
}, function (reason) {
reject(reason)
})
})
})
return resPromise
}
Promise.race实现
传入的promise集合中,任意一个被兑现或拒绝。则立即兑现返回的promise
MyPromise.race = function (promiseList) {
const resPromise = new MyPromise(function (resolve, reject) {
const maxCount = promiseList.length
let count = promiseList.length
if(maxCount === 0) {
return resolve()
} else {
while(count--) {
const index = maxCount - count
MyPromise.resolve(promiseList[index]).then((value)=> {
return resolve(value)
}, function(reason) {
return reject(reason)
})
}
}
})
return resPromise
}
Promise.allSettled
当传入的promise集合全部兑现或拒绝,才兑现返回的promise
MyPromise.allSettled = function (promiseList) {
return new MyPromise(function (resolve) {
let length = promiseList.length;
let result = []
let count = 0
if(length === 0) {
return resolve()
} else {
for(let i = 0; i < length; i++) {
const currentPromise = MyPromise.resolve(promiseList[i])
currentPromise.then((value)=>{
count++
result[i] = {
status: FULFILLED,
value
}
if(count === length) {
return resolve(result)
}
}, (reason)=>{
count++
result[i] = {
status: REJECTED,
reason
}
if(count === length) {
return resolve(result)
}
})
}
}
})
}
完整代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function MyPromise(fn) {
let that = this
this.status = PENDING;
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
function resolve(value) {
if (that.status === PENDING) {
that.status = FULFILLED;
that.value = value
that.onFulfilledCallbacks.forEach((callback) => {
callback(that.value)
})
}
}
function reject(reason) {
if (that.status === PENDING) {
that.status = REJECTED
that.reason = reason
that.onRejectedCallbacks.forEach((callback) => {
callback(that.reason)
})
}
}
try {
fn(resolve, reject)
} catch (e) {
reject(e)
}
}
function resolvePromise(promise, x, resolve, reject) {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
// 这是为了防止死循环
if (promise === x) {
return reject(new TypeError('The promise and the return value are the same'));
}
if (x instanceof MyPromise) {
x.then(function (y) {
resolvePromise(promise, y, resolve, reject)
}, reject)
} else if (typeof x === 'object' || typeof x === 'function') {
if (x === null) {
return resolve(x);
}
let then;
let called = false;
// thenable 兼容
try {
then = x.then;
} catch (e) {
return reject(error);
}
if (typeof then === 'function') {
try {
then.call(x, function (y) {
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
}, function (r) {
if(called) return;
called = true
reject(r)
})
} catch(e) {
if(called) return;
reject(e)
}
} else {
resolve(x)
}
}
}
function timeoutWrapper(fn) {
return function () {
const time = setTimeout(()=>{
fn()
clearTimeout(time)
}, 0)
}
}
// 发布订阅实现then回调调用时机
// then的返回值 。then的返回值必须是一个promise
// 如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)。
MyPromise.prototype.then = function (onFulfilled, onRejected) {
let realOnFulfilled = onFulfilled
const that = this
if (typeof realOnFulfilled !== 'function') {
realOnFulfilled = function (value) {
return value
}
}
let realOnRejected = onRejected
if (typeof realOnRejected !== 'function') {
realOnRejected = function (reason) {
if (reason instanceof Error) {
throw reason;
} else {
throw new Error(reason)
}
}
}
let _reject;
let _resolve;
let _returnVal;
let _fullfill;
const _promise = new MyPromise(function (resolve, reject) {
_reject = reject;
_resolve = resolve
})
// .then的返回值必须为一个promise
if (this.status === FULFILLED) {
_fullfill = timeoutWrapper(()=>{
try {
// 如果传入的onFulfilled是一个值,则需要将这个值传递给下一个.then
if (typeof onFulfilled !== 'function') {
_resolve(this.value)
} else {
_returnVal = realOnFulfilled(this.value)
_resolve(this.value)
}
} catch (e) {
_reject(e)
}
})
}
if (this.status === REJECTED) {
_fullfill = timeoutWrapper(()=>{
try {
if (typeof onRejected !== 'function') {
_reject(this.reason)
} else {
_returnVal = realOnRejected(this.reason)
_resolve()
}
} catch (e) {
_reject(e)
}
})
}
if (this.status === PENDING) {
this.onFulfilledCallbacks.push(timeoutWrapper(function () {
try {
_returnVal = realOnFulfilled(that.value)
} catch (e) {
_reject(e)
}
}));
this.onRejectedCallbacks.push(timeoutWrapper(function () {
try {
_returnVal = realOnRejected(that.reason)
} catch (e) {
_reject(e)
}
}));
}
_fullfill && _fullfill()
resolvePromise(_promise, _returnVal, _resolve, _reject)
return _promise
}
MyPromise.prototype.catch = function (onRejected) {
this.then(null, onRejected)
}
MyPromise.prototype.finally = function (callback) {
return this.then(function (val) {
return MyPromise.resolve(callback()).then(function () {
return val
})
}, function() {
return MyPromise.resolve(callback()).then(function () {
throw error
});
})
}
MyPromise.resolve = function (parameter) {
if(parameter instanceof MyPromise) {
return parameter
}
return new MyPromise((resolve)=>{
resolve(parameter)
})
}
MyPromise.reject = function (reason) {
return new MyPromise((_, reject)=>{
reject(reason)
})
}
MyPromise.all = function(promiseList) {
const resPromise = new MyPromise(function (resolve, reject) {
let count = 0
let result = []
let length = promiseList.length
if(length === 0) {
return resolve()
}
promiseList.forEach(function(promise, index) {
MyPromise.resolve(promise).then(function(value) {
count++;
result[index] = value;
if(count === length) {
resolve(result)
}
}, function (reason) {
reject(reason)
})
})
})
return resPromise
}
MyPromise.race = function (promiseList) {
const resPromise = new MyPromise(function (resolve, reject) {
const maxCount = promiseList.length
let count = promiseList.length
if(maxCount === 0) {
return resolve()
} else {
while(count--) {
const index = maxCount - count
MyPromise.resolve(promiseList[index]).then((value)=> {
return resolve(value)
}, function(reason) {
return reject(reason)
})
}
}
})
return resPromise
}
MyPromise.allSettled = function (promiseList) {
return new MyPromise(function (resolve) {
let length = promiseList.length;
let result = []
let count = 0
if(length === 0) {
return resolve()
} else {
for(let i = 0; i < length; i++) {
const currentPromise = MyPromise.resolve(promiseList[i])
currentPromise.then((value)=>{
count++
result[i] = {
status: FULFILLED,
value
}
if(count === length) {
return resolve(result)
}
}, (reason)=>{
count++
result[i] = {
status: REJECTED,
reason
}
if(count === length) {
return resolve(result)
}
})
}
}
})
}
// 测试MyPromise类的resolve方法
const testResolve = function() {
const promise = MyPromise.resolve('Resolved!')
promise.then((data) => {
console.log('resolve方法测试结果:', data)
})
}
// 测试MyPromise类的reject方法
const testReject = function() {
const promise = MyPromise.reject('Rejected!')
promise.catch((err) => {
console.log('reject方法测试结果:', err)
})
}
// 测试MyPromise类的then方法
const testThen = function() {
const promise = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Resolved!'); }, 1000); })
promise.then((data) => {
console.log('then方法测试结果:', data)
})
}
// 测试MyPromise类的catch方法
const testCatch = function() {
const promise = new MyPromise((resolve, reject) => { setTimeout(() => { reject('Rejected!'); }, 1000); })
promise.catch((err) => {
console.log('catch方法测试结果:', err)
})
}
// 测试MyPromise类的finally方法
const testFinally = function() {
const promise = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Resolved!'); }, 1000); })
promise.finally(() => {
console.log('finally方法测试结果:执行完毕')
})
}
// 测试MyPromise类的all方法
const testAll = function() {
const promise1 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 1 Resolved!'); }, 1000); })
const promise2 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 2 Resolved!'); }, 2000); })
const promise3 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 3 Resolved!'); }, 3000); })
const promiseList = [promise1, promise2, promise3]
MyPromise.all(promiseList).then((data) => {
console.log('all方法测试结果:', data)
})
}
// 测试MyPromise类的race方法
const testRace = function() {
const promise1 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 1 Resolved!'); }, 1000); })
const promise2 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 2 Resolved!'); }, 2000); })
const promise3 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 3 Resolved!'); }, 3000); })
const promiseList = [promise1, promise2, promise3]
MyPromise.race(promiseList).then((data) => {
console.log('race方法测试结果:', data)
})
}
// 测试MyPromise类的allSettled方法
const testAllSettled = function() {
const promise1 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 1 Resolved!'); }, 1000); })
const promise2 = new MyPromise((resolve, reject) => { setTimeout(() => { reject('Promise 2 Rejected!'); }, 2000); })
const promise3 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('Promise 3 Resolved!'); }, 3000); })
const promiseList = [promise1, promise2, promise3]
MyPromise.allSettled(promiseList).then((data) => {
console.log('allSettled方法测试结果:', data)
})
}
// 执行所有测试
testResolve()
testReject()
testThen()
testCatch()
testFinally()
testAll()
testRace()
testAllSettled()