I recently read some articles about the internal implementation of the Promise keyword in javascript, and tried to implement the Promise myself. Here are some relevant notes.
A Promise is an object that can return the result of an asynchronous operation at some point in the future. This can be the result of successfully resolved resolved or the cause of an error during the ResovLED resolution process. It has three states during execution:
- This will be Fulfilled and the result state will be correctly resolved (call resolve())
- Reject complete but result parse error status (call reject())
- Pending is neither complete nor rejected
Internally, promises manage the three life cycle transitions by defining a set of state machines, so the first step is to define the basic structure of the state machine
function PromiseDemo() { this.PENDING = 0; this.FULFILLED = 1; this.REJECTED = 2; this.handlers = []; this.state = this.PENDING; // Initialize the state of the Promise. The initial state is PENDING this.value = null; // This is a big pity; // This is a big pity; // This is a big pity; // This is a big pity; // This is a big pity; this.value = result; }; const rejected = (error) => { this.state = this.REJECTED; this.value = error; }; }Copy the code
Now that the Promise’s state machine is defined, a Promise is in the completed state (resolve or Rejected) if its state is not pending. Once the state of a Promise has been switched from pending (resolve or Reject), it will not be changed, and a call to resolve or Reject will have no effect. This stability in the completed state is an important feature of promises.
The standard Promise is defined by Promises/A+ Specification
) community-made specification, a brief summary of the following rules that Promise implementation should follow:
- A Promise is one that can provide compliance with standards
.then()
Method object - A pending Promise can be fulfilled or rejected
- This is a big pity or the Promise of the rejected state cannot go to any other state after it is fulfilled
- Once a Promise is fulfilled, it must have a value (possibly undefined) that cannot be changed
According to these principles, when defining the Promise state machine, three states are defined, which will be fulfilled and rejected () and rejection ().
Now that we have a method for transitioning the Promise state, where does it change for the caller? Let’s start with an example of using promises
const wait= () = > {return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello'); }, 0)})}wait().then((result) => {
console.log(result); // hello
})
Copy the code
Wait functions have asynchronous operations such as setTimeout, wrapped with a Promise, and then called resolve or Reject when the asynchronous operation is finished. So the state change is triggered by the callback passed in when the Promise is instantiated. Now define resolve and reject for the incoming callback
/** *@params {fn} promise instantiates the incoming callback to receive resolve and reject */functionPromiseDemo(fn) { this.PENDING = 0; this.FULFILLED = 1; this.REJECTED = 2; this.handlers = []; This. State = PENDING; // Initialize the state of the Promise. The initial state is PENDING this.value = null; // This is a big pity /** * then() this is a big pity and onRejected // Const handler = (handle) => {// This is a big pity.if (this.state === this.PENDING) {
this.handlers.push(handle);
} else {
if (
this.state === this.FULFILLED &&
typeof handle.onFulfilled === "function"
) {
handle.onFulfilled(this.value);
}
if (
this.state === this.REJECTED &&
typeof handle.onRejected === "function") { handle.onRejected(this.value); }}}; const fulfilled = (result) => { this.state = this.FULFILLED; this.value = result; this.handlers.forEach(handler); this.handlers = null; }; const rejected = (error) => { this.state = this.REJECTED; this.value = error; this.handlers.forEach(handler); this.handlers = null; };functionResolve (value) {// Fulfill if errors occur during the fulfill process, you need to switch to the REJECTED state try {fulfilled(value); }catch(err) { rejected(err); This will be a pity (result); this will be a pity (result); this will be a pity (result); } catch (error) { rejected(error); } }, (error) => { rejected(error); }) this. Done = (onFulfilled, onRejected) => {// Make sure that the result processing operation and the asynchronous operation inside the Promise remain asynchronoussetTimeout(() => {
handler({ onFulfilled,onRejected })
}, 0)
}
Copy the code
SetTimeout is used here to ensure that all operations within the Promise are asynchronous, that is, if we pass in no asynchronous operation within the Promise, we can guarantee that it will be output asynchronously, but generally we will not use the Promise if there is no asynchronous operation
The.done method implemented here is primarily used for the.then method. .then does the same thing as.done, printing the result of an asynchronous operation, except that.then re-constructs a Promise in the process when it executes. We usually call.then
promise.then( onFulfilled? : Function, onRejected? : Function ) => PromiseCopy the code
Promises/A+ To make Promises/A+ real,.then follows these rules:
onFulfilled()
andonRejected()
This parameter is optional- if
onFulfilled
oronRejected()
Not functions, they’re going to be ignored onFulfilled()
Will be called when the Promise state is fulfilled, and Promsie will be used asynchronouslyvalue
As its first parameter, it cannot be called before the Promise has transitioned to the depressing stateonRejected()
Is called when the Promise state is Rejected, and takes the exception reason for the transition to Rejected as the first argument. It cannot be called before the Promise has transitioned to the Rejected stateonFulfilled()
andonRejected()
Cannot be called more than once.then()
This Promise can be invoked many times in a Promise. When the Promise is fulfilled, all of its own will be fulfilledonFullfilled()
Callbacks must all follow them in.then
Call the sequential execution inside. Also when the Promise is in the Rejected state, all its ownonRejected()
Callbacks must follow them in.then
Call the sequential execution inside.then()
You have to return a PromisePromise2 = Promise1.then(onFulfilled, onRejected) Copy the code
- if
onFulfilled()
oronRejected()
Returns ax
Values, andx
It’s a Promise, then Promise2 will make peacex
And the state ofvalue
Be consistent, otherwise Promise2 willx
The value of switches to the depressing state - if
onFulfilled()
oronRejected()
Throw an exceptione
Promise2 must transition to the Rejected state and be usede
As a reason to - if
onFulfilled()
Is not a function and promise1 transitions to the depressing state, promise2 must use the same values as promise1 to transition to the depressing state
- if
- if
onRejected()
Promise1 is not a function and transitions to the Rejected state, promise2 must use the same exception reason as promise1 to transition to the Rejected state
Follow the.then() principle above and implement it briefly.
this.then = (onFulfilled, onRejected) => {
return new Promise((resolve, reject) => {
this.done((result) => {
if(typeof onFulfilled === 'function'This is a big pity (result); // This is a big pity (pity); // This is a big pity (pity); }catch(err) { reject(err); }}else {
resolve(result);
}
}, (err) => {
if(typeof onRejected === 'function') { try { resolve(onRejected(err)); }catch(ex) { reject(ex); }}else{ reject(err); }})})}Copy the code
A new asynchronous operation will be wrapped in.then, and the result of that asynchronous operation will be received in the next.then(). Which is the penultimate of the.then() principle mentioned above. First we need to wrap the fn() call and add a helper function to getThen() to get the asynchronous operations in.then()
/** / const getThen = (value) => {const t = typeof value;if (t && (t === "object" || t === "function")) {
const then = value.then;
if (typeof then= = ="function") {
console.log('functionThen'.then);
return then; }}return null;
};
const doResolve = (fn, onFulfilled, onRejected) => { try { fn( (result) => { try { onFulfilled(result); } catch (error) { onRejected(error); } }, (error) => { onRejected(error); }); } catch (error) { onRejected(error); }}; const resolve = (result) => { try { constthen = getThen(result);
if (then) {
doResolve(then, resolve, rejected);
return; } fulfilled(result); } catch (error) { rejected(error); }};Copy the code
Complete code implementation:
function MyPromise(fn) {
this.PENDING = 0;
this.FULFILLED = 1;
this.REJECTED = 2;
this.handlers = [];
this.state = this.PENDING;
this.value = null;
const handler = (handle) => {
if (this.state === this.PENDING) {
this.handlers.push(handle);
} else {
if (
this.state === this.FULFILLED &&
typeof handle.onFulfilled === "function"
) {
handle.onFulfilled(this.value);
}
if (
this.state === this.REJECTED &&
typeof handle.onRejected === "function") { handle.onRejected(this.value); }}}; const fulfilled = (result) => { this.state = this.FULFILLED; this.value = result; this.handlers.forEach(handler); this.handlers = null; }; const rejected = (error) => { this.state = this.REJECTED; this.value = error; this.handlers.forEach(handler); this.handlers = null; }; /** / const getThen = (value) => {const t = typeof value;if (t && (t === "object" || t === "function")) {
const then = value.then;
if (typeof then= = ="function") {
console.log('functionThen'.then);
return then; }}return null;
};
const doResolve = (fn, onFulfilled, onRejected) => { try { fn( (result) => { try { onFulfilled(result); } catch (error) { onRejected(error); } }, (error) => { onRejected(error); }); } catch (error) { onRejected(error); }}; const resolve = (result) => { try { constthen = getThen(result);
if (then) {
doResolve(then, resolve, rejected);
return; } fulfilled(result); } catch (error) { rejected(error); }};doResolve(fn, resolve, rejected); This. Done = (onFulfilled, onRejected) => {// Make sure that the internal operations are asynchronoussetTimeout(() => {
handler({ onFulfilled, onRejected });
}, 0);
};
this.then = (onFulfilled, onRejected) => {
return new MyPromise((resolve, reject) => {
return this.done(
(value) => {
if (typeof onFulfilled === "function") {// We use so many callbacks in order to get the result of the asynchronous operation in the callback of the result processing.return resolve(onFulfilled(value));
} catch (error) {
returnreject(error); }}else {
return resolve(value);
}
},
(error) => {
if (typeof onRejected === "function") {
try {
return resolve(onRejected(error));
} catch (error) {
returnreject(error); }}else {
returnreject(error); }}); }); }; } exports.myPromise = MyPromise;Copy the code
conclusion
- Promise internally defines the state machine to realize the transition of pending, depressing and Rejected states. The pending state refers to processing internal asynchronous operations. The fulfilled and Rejected indicate the result of successful processing and the failure cause after the asynchronous operation is completed
- Promises need to have standards that conform to the PromiseA+ specification
.then()
Method to process the result of the asynchronous output from a Promise.
Refer to the article
- Medium.com/javascript-…
- promisesaplus.com/
- www.promisejs.org/implementin…