Promise is a constructor for asynchronous requests introduced by es6 to help solve the problem of callback hell. The following content will customize the implementation of promise, which only includes basic use, so some boundary conditions are not taken into account.
If you are not familiar with promise usage, you can move
Understanding and use of Promise (I)
Understanding and use of Promise (II)
executor
Firstly, the basic structure of promise is established
Define constructor
The executor part of promise is directly executed in the main thread
class icePromise { constructor(executor){ const resolve = () => { console.log('resolve Called') } const reject = () => { console.log('reject Called') } } executor(resolve, reject) } const promise = new icePromise((resolve, reject)=>{ resolve() })
Define status
1. Define constants
const STATUS_PENDING = 'pending' const STATUS_FULFILLED = 'fulfilled' const STATUS_REJECTED = 'rejected'
2. When you create an instance through a constructor, you should need a state, so it is defined in the class
this.status = STATUS_PENDING
3. State judgment in the resolve and reject methods
When it is pending, the resolve or reject method can be executed. Modify the status before execution
then method
In onFulfilled/onRejected, the callback function in then will be executed, and the two functions will be bound to the properties of the instance
1. Define a then method in the class
then(onFulfilled, onRejected){ this.onFulfilled = onFulfilled this.onRejected = onRejected }
2. Execute this. In resolve and reject, respectively Onfulfilled and this In onrejected
An error will be reported at this time, because the executor will execute immediately, and the function in then is a micro task,
It will be executed after the main thread completes execution
3. Queue microtask is added to resolve and reject
The first version of the overall architecture is completed
const STATUS_PENDING = "pending"; const STATUS_FULFILLED = "fulfilled"; const STATUS_REJECTED = "rejected"; class IcePromise { constructor(executor) { this.status = STATUS_PENDING; const resolve = (value) => { if (this.status === STATUS_PENDING) { this.status = STATUS_FULFILLED; queueMicrotask(() => { this.onFulfilled(value); }); } }; const reject = (reason) => { if (this.status === STATUS_PENDING) { this.status = STATUS_REJECTED; queueMicrotask(() => { this.onRejected(reason); }); } }; executor(resolve, reject); } then(onFulfilled, onRejected) { this.onFulfilled = onFulfilled; this.onRejected = onRejected; } } const promise = new IcePromise((resolve, reject) => { resolve("resolve"); reject("reject"); }); promise.then( (value) => { console.log("success1", value); }, (reason) => { console.log("fail1", reason); } ); promise.then( (value) => { console.log("success2", value); }, (reason) => { console.log("fail2", reason); } );
The state of promise is returned twice, and only the first resolve is executed. The successful or failed functions in the then method are also correct
However, the problem is that the then method is executed twice, and only the second one is executed. The then method is optimized below.
then
Solve the problem of calling the then method multiple times
1. Variables defined in the constructor are used to collect all successful / failed callback functions
this.onFulfilledCallbacks = [] this.onRejectedCallbacks = []
2. Add to the array by push ing in the then method
this.onFulfilledCallbacks.push() this.onRejectedCallbacks.push()
3. Traverse in resolve and reject
this. Onfulfilled callbacks and this Methods in onrejectedcallbacks
The code is as follows
const STATUS_PENDING = "pending"; const STATUS_FULFILLED = "fulfilled"; const STATUS_REJECTED = "rejected"; class IcePromise { constructor(executor) { this.status = STATUS_PENDING; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { this.onResolvedCallbacks.forEach((fn) => { fn(value); }); }); } }; const reject = (reason) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { this.onRejectedCallbacks.forEach((fn) => { fn(reason); }); }); } }; executor(resolve, reject); } then(onFulfilled, onRejected) { this.onResolvedCallbacks.push(onFulfilled); this.onRejectedCallbacks.push(onRejected); } } // Test code const promise = new IcePromise((resolve, reject) => { resolve("resolve---"); reject("----reject"); }); promise.then( (value) => { console.log("res1", value); }, (reason) => { console.log("err1", reason); } ) promise.then( (value) => { console.log("res2", value); }, (reason) => { console.log("err2", reason); } ); // Call after determining the status setTimeout(() => { promise.then( (res) => { console.log("res3", res); }, (err) => { console.log("err3", err); } ); }, 1000);
Solve the problem of multiple calls of then, but there are still other problems. One is that the resolve and reject methods are executed at the same time, and the other is that the then method of promise, which is delayed by a timer, does not output a response result
Solve the problem of delayed call
1. Save value and reason
this.value = undefined this.reason = undefined
The resolve and reject methods are given to this Value and this Reason assignment
2. State judgment in then method
When the status is pending, continue to add functions to onfulledcallbacks and onRejectedCallbacks arrays; When the status is not pending, the onFulfilled or onRejected methods are executed directly
if (this.status === STATUS_FULFILLED && onFulfilled) { onFulfilled(this.value); } if (this.status === STATUS_REJECTED && onRejected) { onRejected(this.reason); } if (this.status === STATUS_PENDING) { this.onResolvedCallbacks.push(onFulfilled); this.onRejectedCallbacks.push(onRejected); }
3. Change of pending status
① If it is judged not to be pending in queueMicrotask, return
② Modify pending status
const STATUS_PENDING = "pending"; const STATUS_FULFILLED = "fulfilled"; const STATUS_REJECTED = "rejected"; class IcePromise { constructor(executor) { this.status = STATUS_PENDING; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; this.value = undefined; this.reason = undefined; const resolve = (value) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_FULFILLED; this.value = value; this.onResolvedCallbacks.forEach((fn) => { fn(this.value); }); }); } }; const reject = (reason) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => { fn(this.reason); }); }); } }; executor(resolve, reject); } then(onFulfilled, onRejected) { if (this.status === STATUS_FULFILLED && onFulfilled) { onFulfilled(this.value); } if (this.status === STATUS_REJECTED && onRejected) { onRejected(this.reason); } if (this.status === STATUS_PENDING) { this.onResolvedCallbacks.push(onFulfilled); this.onRejectedCallbacks.push(onRejected); } } } // Test code const promise = new IcePromise((resolve, reject) => { resolve("resolve---"); reject("----reject"); }); promise.then( (value) => { console.log("res1", value); }, (reason) => { console.log("err1", reason); } ) promise.then( (value) => { console.log("res2", value); }, (reason) => { console.log("err2", reason); } ); // Call after determining the status setTimeout(() => { promise.then( (res) => { console.log("res3", res); }, (err) => { console.log("err3", err); } ); }, 1000); promise.then( (value) => { console.log("res4", value); }, (reason) => { console.log("err4", reason); } ).then( (value) => { console.log("res5", value); }, (reason) => { console.log("err5", reason); } )
It solves the problems of multiple calls of resolve and reject and delayed call of timer, but it is found that then cannot make chain call at this time
Solve the problem of chain call
1. The then method returns a new icePromise and puts the judgment logic in it
2,this. Onfulfilled callbacks and this Onrejectedcallbacks passes in the callback function,
The callback function returns the execution result of the resolve or reject function
3. Encapsulating tool functions for handling try catch
const STATUS_PENDING = "pending"; const STATUS_FULFILLED = "fulfilled"; const STATUS_REJECTED = "rejected"; const respondWithCatchError = (fn, value, resolve, reject) => { try { const result = fn(value); resolve(result); } catch (error) { reject(error); } }; class IcePromise { constructor(executor) { this.status = STATUS_PENDING; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; this.value = undefined; this.reason = undefined; const resolve = (value) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_FULFILLED; this.value = value; this.onResolvedCallbacks.forEach((fn) => { fn(this.value); }); }); } }; const reject = (reason) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => { fn(this.reason); }); }); } }; executor(resolve, reject); } then(onFulfilled, onRejected) { return new Promise((resolve, reject) => { if (this.status === STATUS_FULFILLED && onFulfilled) { respondWithCatchError(onFulfilled, this.value, resolve, reject); } if (this.status === STATUS_REJECTED && onRejected) { respondWithCatchError(onRejected, this.reason, resolve, reject); } if (this.status === STATUS_PENDING) { this.onResolvedCallbacks.push(() => { respondWithCatchError(onFulfilled, this.value, resolve, reject); }); this.onRejectedCallbacks.push(() => { respondWithCatchError(onRejected, this.reason, resolve, reject); }); } }); } } // Test code const promise = new IcePromise((resolve, reject) => { resolve("resolve---"); reject("----reject"); }); promise .then( (value) => { console.log("res1", value); }, (reason) => { console.log("err1", reason); } ) .then( (value) => { console.log("res2", value); }, (reason) => { console.log("err2", reason); } ) .then( (res) => { console.log("res3", res); }, (err) => { console.log("err3", err); } );
At this point, the then function can be called in a chain, and the basic functions have been realized~
catch
The catch function receives a failed callback
1. Call the then method and add the onRejected method to the callback of the second promise
catch(onRejected){ this.then(null, onRejected) }
2. In the then method, judge the onRejected passed in, and throw an exception when there is no delivery
const defaultOnRejected = (reason) => { throw reason; }; onRejected = onRejected || defaultOnRejected;
The overall implementation is as follows
const STATUS_PENDING = "pending"; const STATUS_FULFILLED = "fulfilled"; const STATUS_REJECTED = "rejected"; const respondWithCatchError = (fn, value, resolve, reject) => { try { const result = fn(value); resolve(result); } catch (error) { reject(error); } }; class IcePromise { constructor(executor) { this.status = STATUS_PENDING; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; this.value = undefined; this.reason = undefined; const resolve = (value) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_FULFILLED; this.value = value; this.onResolvedCallbacks.forEach((fn) => { fn(this.value); }); }); } }; const reject = (reason) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => { fn(this.reason); }); }); } }; executor(resolve, reject); } then(onFulfilled, onRejected) { const defaultOnFulfilled = (value) => { return value; }; const defaultOnRejected = (reason) => { throw reason; }; onFulfilled = onFulfilled || defaultOnFulfilled; onRejected = onRejected || defaultOnRejected; return new Promise((resolve, reject) => { if (this.status === STATUS_FULFILLED && onFulfilled) { respondWithCatchError(onFulfilled, this.value, resolve, reject); } if (this.status === STATUS_REJECTED && onRejected) { respondWithCatchError(onRejected, this.reason, resolve, reject); } if (this.status === STATUS_PENDING) { this.onResolvedCallbacks.push(() => { respondWithCatchError(onFulfilled, this.value, resolve, reject); }); this.onRejectedCallbacks.push(() => { respondWithCatchError(onRejected, this.reason, resolve, reject); }); } }); } catch(onRejected) { this.then(null, onRejected); } } const promise = new IcePromise((resolve, reject) => { reject("----reject"); resolve("resolve---"); }); // Test code promise .then((value) => { console.log("res1", value); }) .then((value) => { console.log("res2", value); }) .catch((error) => { console.log("catch", error); });
The results are as follows
finally
finally method will be executed after the resolve or reject method is executed
finally(onFinally){ this.then(()=>{ onFinally() }, ()=>{ onFinally() }) }
The overall implementation is as follows
const STATUS_PENDING = "pending"; const STATUS_FULFILLED = "fulfilled"; const STATUS_REJECTED = "rejected"; const respondWithCatchError = (fn, value, resolve, reject) => { try { const result = fn(value); resolve(result); } catch (error) { reject(error); } }; class IcePromise { constructor(executor) { this.status = STATUS_PENDING; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; this.value = undefined; this.reason = undefined; const resolve = (value) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_FULFILLED; this.value = value; this.onResolvedCallbacks.forEach((fn) => { fn(this.value); }); }); } }; const reject = (reason) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => { fn(this.reason); }); }); } }; executor(resolve, reject); } then(onFulfilled, onRejected) { const defaultOnFulfilled = (value) => { return value; }; const defaultOnRejected = (reason) => { throw reason; }; onFulfilled = onFulfilled || defaultOnFulfilled; onRejected = onRejected || defaultOnRejected; return new Promise((resolve, reject) => { if (this.status === STATUS_FULFILLED && onFulfilled) { respondWithCatchError(onFulfilled, this.value, resolve, reject); } if (this.status === STATUS_REJECTED && onRejected) { respondWithCatchError(onRejected, this.reason, resolve, reject); } if (this.status === STATUS_PENDING) { this.onResolvedCallbacks.push(() => { respondWithCatchError(onFulfilled, this.value, resolve, reject); }); this.onRejectedCallbacks.push(() => { respondWithCatchError(onRejected, this.reason, resolve, reject); }); } }); } catch(onRejected) { this.then(null, onRejected); } finally(onFinally) { this.then( () => { onFinally(); }, () => { onFinally(); } ); } } // Test code const promise = new IcePromise((resolve, reject) => { reject("----reject"); }); promise .then( (value) => { console.log("res1", value); }, (reason) => { console.log("err1", reason); } ) .finally(() => { console.log("finally"); });
resolve/reject
resolve and reject are Promise class methods, which can also be implemented by calling the then method
static resolve(value){ return new icePromise((resolve)=>resolve(value)) } static reject(reason){ return new icePromise((resolve, reject)=>reject(reason)) }
The complete implementation is as follows
const STATUS_PENDING = "pending"; const STATUS_FULFILLED = "fulfilled"; const STATUS_REJECTED = "rejected"; const respondWithCatchError = (fn, value, resolve, reject) => { try { const result = fn(value); resolve(result); } catch (error) { reject(error); } }; class IcePromise { constructor(executor) { this.status = STATUS_PENDING; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; this.value = undefined; this.reason = undefined; const resolve = (value) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_FULFILLED; this.value = value; this.onResolvedCallbacks.forEach((fn) => { fn(this.value); }); }); } }; const reject = (reason) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => { fn(this.reason); }); }); } }; executor(resolve, reject); } then(onFulfilled, onRejected) { const defaultOnFulfilled = (value) => { return value; }; const defaultOnRejected = (reason) => { throw reason; }; onFulfilled = onFulfilled || defaultOnFulfilled; onRejected = onRejected || defaultOnRejected; return new Promise((resolve, reject) => { if (this.status === STATUS_FULFILLED && onFulfilled) { respondWithCatchError(onFulfilled, this.value, resolve, reject); } if (this.status === STATUS_REJECTED && onRejected) { respondWithCatchError(onRejected, this.reason, resolve, reject); } if (this.status === STATUS_PENDING) { this.onResolvedCallbacks.push(() => { respondWithCatchError(onFulfilled, this.value, resolve, reject); }); this.onRejectedCallbacks.push(() => { respondWithCatchError(onRejected, this.reason, resolve, reject); }); } }); } catch(onRejected) { this.then(null, onRejected); } finally(onFinally) { this.then( () => { onFinally(); }, () => { onFinally(); } ); } static resolve(value) { return new Promise((onResolve) => { onResolve(value); }); } static reject(reason) { return new Promise((onResolve, onRejected) => { onRejected(reason); }); } } // Test code const promise = Promise.reject(1); promise .then( (value) => { console.log("res1", value); }, (reason) => { console.log("err1", reason); } )
The results are as follows
all/allSettled
all and allSettled methods are promise class methods
1. all method
As long as one promise method executes reject, it will execute reject. When all promises return resolve, the resolve method will be executed.
2. allSettled method
When all promises are completed, the resolve method is executed to return the status and results of all promises.
static all(promise){ return new icePromise((resolve, reject)=>{ const values = [] promises.forEach(promise => { promise.then(res => { values.push(res) if (values.length === promises.length) { resolve(values) } }, err => { reject(err) }) }) }) }) } static allSettled(promise){ return new icePromise((resolve, reject)=>{ const values = [] promise.then(res=>{ values.push({ status: '', value: '' }) if(values.length === promise.length){ resolve(values) } }, err=>{ values.push({ status: '', value: '' }) if(values.length === promise.length){ resolve(values) } }) }) }
The complete implementation is as follows
const STATUS_PENDING = "pending"; const STATUS_FULFILLED = "fulfilled"; const STATUS_REJECTED = "rejected"; const respondWithCatchError = (fn, value, resolve, reject) => { try { const result = fn(value); resolve(result); } catch (error) { reject(error); } }; class IcePromise { constructor(executor) { this.status = STATUS_PENDING; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; this.value = undefined; this.reason = undefined; const resolve = (value) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_FULFILLED; this.value = value; this.onResolvedCallbacks.forEach((fn) => { fn(this.value); }); }); } }; const reject = (reason) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => { fn(this.reason); }); }); } }; executor(resolve, reject); } then(onFulfilled, onRejected) { const defaultOnFulfilled = (value) => { return value; }; const defaultOnRejected = (reason) => { throw reason; }; onFulfilled = onFulfilled || defaultOnFulfilled; onRejected = onRejected || defaultOnRejected; return new Promise((resolve, reject) => { if (this.status === STATUS_FULFILLED && onFulfilled) { respondWithCatchError(onFulfilled, this.value, resolve, reject); } if (this.status === STATUS_REJECTED && onRejected) { respondWithCatchError(onRejected, this.reason, resolve, reject); } if (this.status === STATUS_PENDING) { this.onResolvedCallbacks.push(() => { respondWithCatchError(onFulfilled, this.value, resolve, reject); }); this.onRejectedCallbacks.push(() => { respondWithCatchError(onRejected, this.reason, resolve, reject); }); } }); } catch(onRejected) { this.then(null, onRejected); } finally(onFinally) { this.then( () => { onFinally(); }, () => { onFinally(); } ); } static resolve(value) { return new Promise((onResolve) => { onResolve(value); }); } static reject(reason) { return new Promise((onResolve, onRejected) => { onRejected(reason); }); } static all(promises) { return new Promise((resolve, reject) => { const result = []; promises.forEach((promise) => { promise.then( (value) => { result.push(value); if (result.length === promises.length) { resolve(result); } }, (reason) => { reject(reason); } ); }); }); } static allSettled(promises) { return new Promise((resolve, reject) => { const result = []; promises.forEach((promise) => { promise.then( (value) => { result.push({ status: STATUS_FULFILLED, value, }); if (result.length === promises.length) { resolve(result); } }, (reason) => { result.push({ status: STATUS_REJECTED, reason, }); if (result.length === promises.length) { resolve(result); } } ); }); }); } } // Test code const promise1 = IcePromise.resolve(1); const promise2 = new IcePromise((resolve, reject) => { setTimeout(() => { reject(2); }); }); const promise3 = IcePromise.resolve(3); IcePromise.all([promise1, promise2, promise3]).then( (value) => { console.log("res1", value); }, (reason) => { console.log("err1", reason); } ); IcePromise.allSettled([promise1, promise2, promise3]).then( (value) => { console.log("res2", value); }, (reason) => { console.log("err2", reason); } );
The results are as follows
race/any
race and any are both promise class methods.
1. race method
As long as a promise execution is completed, the result of its execution will be returned
2. any method
① If there is a fully filled status, wait until the fully filled execution is completed, execute resolve, and the result is value
② If all the promises are rejected, an AggregateError error will be reported after all the promises become rejected.
static race(promises){ return new icePromise((resolve, reject)=>{ promises.forEach(promise=>{ promise.then(resolve, reject) }) }) } static any(promises){ const reasons = [] return new icePromise((resolve, reject)=>{ promises.forEach(promise=>{ promise.then(resolve, err=>{ reasons.push(err) if(reasons.length === promises.length){ reject(new AggregateError(reasons)) } }) }) }) }
The overall implementation is as follows
const STATUS_PENDING = "pending"; const STATUS_FULFILLED = "fulfilled"; const STATUS_REJECTED = "rejected"; const respondWithCatchError = (fn, value, resolve, reject) => { try { const result = fn(value); resolve(result); } catch (error) { reject(error); } }; class IcePromise { constructor(executor) { this.status = STATUS_PENDING; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; this.value = undefined; this.reason = undefined; const resolve = (value) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_FULFILLED; this.value = value; this.onResolvedCallbacks.forEach((fn) => { fn(this.value); }); }); } }; const reject = (reason) => { if (this.status === STATUS_PENDING) { queueMicrotask(() => { if (this.status !== STATUS_PENDING) return; this.status = STATUS_REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => { fn(this.reason); }); }); } }; executor(resolve, reject); } then(onFulfilled, onRejected) { const defaultOnFulfilled = (value) => { return value; }; const defaultOnRejected = (reason) => { throw reason; }; onFulfilled = onFulfilled || defaultOnFulfilled; onRejected = onRejected || defaultOnRejected; return new Promise((resolve, reject) => { if (this.status === STATUS_FULFILLED && onFulfilled) { respondWithCatchError(onFulfilled, this.value, resolve, reject); } if (this.status === STATUS_REJECTED && onRejected) { respondWithCatchError(onRejected, this.reason, resolve, reject); } if (this.status === STATUS_PENDING) { this.onResolvedCallbacks.push(() => { respondWithCatchError(onFulfilled, this.value, resolve, reject); }); this.onRejectedCallbacks.push(() => { respondWithCatchError(onRejected, this.reason, resolve, reject); }); } }); } catch(onRejected) { this.then(null, onRejected); } finally(onFinally) { this.then( () => { onFinally(); }, () => { onFinally(); } ); } static resolve(value) { return new Promise((onResolve) => { onResolve(value); }); } static reject(reason) { return new Promise((onResolve, onRejected) => { onRejected(reason); }); } static all(promises) { return new Promise((resolve, reject) => { const result = []; promises.forEach((promise) => { promise.then( (value) => { result.push(value); if (result.length === promises.length) { resolve(result); } }, (reason) => { reject(reason); } ); }); }); } static allSettled(promises) { return new Promise((resolve, reject) => { const result = []; promises.forEach((promise) => { promise.then( (value) => { result.push({ status: STATUS_FULFILLED, value, }); if (result.length === promises.length) { resolve(result); } }, (reason) => { result.push({ status: STATUS_REJECTED, reason, }); if (result.length === promises.length) { resolve(result); } } ); }); }); } static race(promises) { return new Promise((resolve, reject) => { promises.forEach((promise) => { promise.then(resolve, reject); }); }); } static any(promises) { return new Promise((resolve, reject) => { const reasons = []; promises.forEach((promise) => { promise.then(resolve, (reason) => { reasons.push(reason); if (reasons.length === promises.length) { reject(new AggregateError(reasons)); } }); }); }); } } // Test code const promise1 = new IcePromise((resolve, reject) => { setTimeout(() => { reject(1); }); }); const promise2 = IcePromise.reject(2); const promise3 = IcePromise.reject(3); IcePromise.race([promise1, promise2, promise3]).then( (value) => { console.log("res1", value); }, (reason) => { console.log("err1", reason); } ); IcePromise.any([promise1, promise2, promise3]).then( (value) => { console.log("res1", value); }, (reason) => { console.log("err1", reason); } );
The above is all the code of custom promise. There are still many places for developers to master about js advanced. You can see other blog posts I wrote and keep updating~