I'm not going to write this article about the causes of Promise and the problems it solves. I just want to write an article about implementing my own Promise. If there is something wrong with the code and logic, please point it out. That's all. Let's get to the point.
Prerequisite: We need to know that Promise is based on Promises/A+ Canonical. Many of these variables and method names come from here. Let's start with the use of Promise and write a few test examples.
let promise = new Promise((resolve, reject) =>{ // console.log("1"); // resolve("Success"); // reject("fail"); // console.log("2");// First step // reject("fail"); // resolve("Success");// The second step // setTimeout(() => { // resolve("success"); // }, 2000); throw new Error("Manual throwing error");// The fourth step }); promise.then((value) => { console.log("then The first method is:"+value); }, (err) => { console.log("then The second method is:"+err); }) promise.then((value) => { console.log("then The first method is:"+value); }, (err) => { console.log("then The second method is:"+err); }) console.log("3");
First step output
- 1
- 2
- 3
- The first method: Success
- The first method: Success
Second step output
- 3
- The second method: Failure
- The second method: Failure
Step 3 Output
- Three or two seconds later
- The first method: success
- The first method: success
Step 4 Output
- 3
- The second method: Error: manually throw an error
- The second method: Error: manually throw an error
The final output of "success" indicates that the n is executed asynchronously
Based on the above examples, we can deduce the following points:
- Promise is a constructor (using new)
- Promise receives a parameter and the parameter is a function (we call it executor for convenience of description)
- executor executes at new Promise
- new Promise can support asynchronous behavior (step 3)
- executor has two parameters (resolve,reject)
- resolve and reject will not be passed in, indicating that they are provided by Promise content
- Both resolve and reject are functions, and both receive a parameter. Look at the specification: the resolve receive parameter is called value, and the reject receive parameter is called reason.
- There are then methods on each Promise instance
- The then method is asynchronous
- There are two parameters in the then method, onFulfilled and onRejected, which are successful callbacks and failed callbacks, respectively. Look here
- In a Promise, resolve and reject will only execute one. Promise States is mentioned in the specification. You can see that
- An instance of the same promise can call back all successful methods when it succeeds and all failed methods when it fails.
- If errors are found, they will go into failure.
It looks a little messy with such a big stuff. We started writing our own Promise based on our conclusions. In the process of writing, the train of thought gradually became clear.
let myPromise = function (executor) { let self = this;//Cache this self.status = 'pending';// Changes in state management state can only be changed from pending to resolved or rejected. One thing cannot succeed and fail at the same time. So resolved and rejected cannot be transformed into each other. self.value = undefined;// Successful values are passed to resolve self.reason = undefined;//Failure causes passed to reject self.onResolvedCallbacks = [];// DepositthenSuccessful callback self.onRejectedCallbacks = []; // DepositthenA failed callback // Let me show you that the third step is to use a timer. After executing the new Promise, it executesthenMethod, at this point will bethenThe method is cached and not executed: at this point the state is still pending. Wait until 2 seconds after the timer, and execute // When resolve|reject, the methods stored in the array are executed sequentially. Reference publish-subscribe mode function resolve(value) { // pending => resolved if (self.status === 'pending') { self.value = value; self.status = 'resolved'; // Successful callbacks that execute caches in turn self.onResolvedCallbacks.forEach(fn => fn(self.value)); } } function reject(reason) { // pending => rejected if (self.status === 'pending') { self.value = value; self.status = 'rejected'; // Failed callbacks that execute caches in turn self.onRejectedCallbacks.forEach(fn => fn(self.reason)); } } try { //executor executes when new Promise executor(resolve, reject); } catch (error) { reject(error);// When an exception occurs in executor, reject is executed directly } } // Each Promise instance hasthenMethod Promise.prototype.then = function (onFulfilled, onRejected) { let self = this; // Resolution was performed if (self.status === 'resolved') { // Perform a successful callback onFulfilled(self.value); } // reject was executed if (self.status === 'rejected') { // Callback for failed execution onRejected(self.reason); } // new Promise can support asynchronism as the default waiting state pending when neither resolve nor reject is executed if (self.status === 'pending') { // Cache successful callback self.onResolvedCallbacks.push(onFulfilled); // Callback for Cache Failure self.onRejectedCallbacks.push(onRejected); } };
Note: This is the simplest version, because the power of Promise is the chain call. We are just embryonic, because of the time relationship. Let's get here first. Next time, we will implement a full version of Promises/A+based on this prototype.
For the first time, I hope you will support me.