catalog: 1,new Promise() 2,.then()And chain call 3,.resolve()and.reject() 4,.all()and.race()
Before tearing Promise by hand, we have to understand the characteristics of Promise.
First of all, Promise is essentially a constructor, which can create Promise object instances to meet the requirements of asynchronous programming.
The Promise features we need to implement in this article are as follows:
1,new Promise((resolve,reject) => {})
As a constructor, the first thing we need to implement is its basic architecture. We first implement the most basic functions:
1)status Storage status value and error Storage successful/Value of failure status 2)resolve and reject Method change state 3)then Method to implement the callback after the state changes
class MyPromise { constructor(executor) { this.status = 'pending'; this.value; // Value of success status this.error; // Value of failure status this.resolveCallbacks = []; this.rejectedCallbacks = []; let resolve = (res) => { } let reject = (err) => { } executor(resolve, reject) } then(onFulfilled, onRejected) { } catch() { } }
Supplement the contents of resolve, reject, then and catch
1)then The most basic function is calling the callback function after the state changes. 2)catch The essence is that there is only failure.then
class MyPromise { constructor(executor) { this.status = 'pending'; this.value; // Value of success status this.error; // Value of failure status this.resolveCallbacks = []; this.rejectedCallbacks = []; let resolve = (res) => { // Change state and execute callback if (this.status === 'pending') { this.status = 'resolved'; this.value = res; } } let reject = (err) => { // Change state and execute callback if (this.status === 'pending') { this.status = 'rejected'; this.error = err; } } executor(resolve, reject) } then(onFulfilled, onRejected) { // The essence is to execute the corresponding callback function if the current state is resolved or rejected if (this.status === 'resolved') { onFulfilled(this.value); } if (this.status === 'rejected') { onRejected(this.error) } } catch(rejected) { return this.then(null, rejected) } }
In this way, we can call the constructor to realize the most basic functions
let pro1 = new MyPromise((resolve, reject) => { resolve('succeed'); }).then(res => { console.log(res); }) console.log(pro1); // succeed
2,. then() and chain call
There are several problems with the writing of then above:
1) When the state change is in an asynchronous function, it is executed at this time then cannot execute the callback immediately. It should be executed together with the change of waiting state
2). The callback in then should be placed in the asynchronous function, so you need to add setTimeout
3). The return value of then must be Promise object, so the result of each asynchronous processing is set to x, and the returned state and value of Promise 2 need to be determined according to X
The resolvePromise method is introduced here to determine the status and value of the returned promise 2.
The initial improvements are as follows:
MyPromise.then(onFulfilled, onRejected) { // The essence is to execute the corresponding callback function if the current state is resolved or rejected let promise2; promise2 = new MyPromise((resolve, reject) => { if (this.status === 'resolved') { setTimeout(() => { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); }) } if (this.status === 'rejected') { setTimeout(() => { let x = onRejected(this.error); resolvePromise(promise2, x, resolve, reject); }) } if (this.status === 'pending') { this.resolveCallbacks.push(() => { setTimeout(() => { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); }) }); this.rejectedCallbacks.push(() => { setTimeout(() => { let x = onRejected(this.error); resolvePromise(promise2, x, resolve, reject); }) }); } }) return promise2; }
The implementation of the function resolvePromise is the core of the Promise implementation. It contains several important rules, which are briefly summarized as follows:
1) If the return value x is a numerical value, Promise in the status of fulfilled is returned, and the value is X
2) If the return value x is a Promise object, continue the recursive call
3) Only when the return value x is a Promise object in the rejected state, the Promise object in the failed state is returned
function resolvePromise(promise2, x, resolve, reject) { // Circular reference error if (x === promise2) { // reject error thrown return reject(new TypeError('Chaining cycle detected for promise')); } // Lock to prevent multiple calls let called; // x is not null and x is an object or function if (x != null && (typeof x === 'object' || typeof x === 'function')) { try { // A + specifies the then method of declaring then = x let then = x.then; // If then is a function, it defaults to promise if (typeof then === 'function') { // Let then execute. The first parameter is this, followed by successful callbacks and failed callbacks then.call(x, y => { // Success and failure can only call one if (called) return; called = true; // If the result of resolve is still promise, continue to execute recursively resolvePromise(promise2, y, resolve, reject); }, err => { // Success and failure can only call one if (called) return; called = true; reject(err);// If you fail, you fail }) } else { resolve(x); // Just succeed directly } } catch (e) { // It also belongs to failure if (called) return; called = true; // If there is an error in getting then, don't continue reject(e); } } else { resolve(x); } }
At the end of the then implementation, some details need to be added. For example, if the incoming onFullfilled is not a function, it will ignore onFullfilled and directly return value; There are also previous calls to add resolve and reject to the callback array
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : error => error;
3,. resolve() and reject()
Promise can also directly call the resolve and reject methods. Its essence is new Promise and then change the state.
MyPromise.resolve = (value) => { return new MyPromise((resolve, reject) => { resolve(value); }) } MyPromise.reject = (value) => { return new MyPromise((resolve, reject) => { reject(value); }) }
4,. all() and race()
First, let's talk about the all() method. Its essence is that it returns a value only when all Promise objects in the parameter are fully completed. It has the following characteristics:
Promise.all([promise1,promise2,...])
1) resolve only when all promise objects in the array are resolved, and the promise object is returned. Value is the successful value of all promise objects
2) If there is a promise object reject, the promise result of the first reject is returned
3) The promises of the parameter only needs to be an iterative object. If the Promise object is not passed, the original value can be returned directly.
MyPromise.all = (promises) => { // promises is an array if (!Array.isArray(promises)) { throw ('Must be an array') } let res = [] return new MyPromise((resolve, reject) => { for (let i = 0; i < promises.length; i++) { if (promises[i].then) { // Is a promise object then promises[i].then(r => { res.push(r); if (res.length === promises.length) { // All success return resolve(res); } }, error => { return reject(error); }) } else { // If it is not a promise object, value is stored directly res.push(promises[i]); if (res.length === promises.length) { // All success return resolve(res); } } } }) }
The race() method is opposite to all. It only returns parameters. All Promise objects change their state the earliest. Its characteristics are as follows:
Promise.race(promises)
1) When the state of a Promise in the array changes to fully / rejected, race returns the Promise state and value
2) Inject the resolve of race directly into the callback of each object in the array
MyPromise.race = (promises) => { return new MyPromise((resolve, reject) => { for (let i = 0; i < promises.length; i++) { promises[i].then(resolve, reject); } }) }
All Promise's hand tears are finished.