At the forefront of
As the saying goes, only by standing on the shoulders of the previous giants can you see further. After reading the promise written by the giants, the meridians and collaterals of the whole body were unblocked, such as being pointed at the Baihui point with golden fingers by our ancestors. Since then, the operation of the size of the Sunday a few points more skilled than usual. However, shoulder Well point, Yongquan point, everyday Haishi, will dull pain. As I pondered over and over, I was probably unable to fully comprehend the subtlety of the previous great master’s article. Solid painstakingly feeling, square get this article. Dare not compare with predecessors, only as a partial complement
Straight guys don’t know how to lay, so they just do it
Here we use ES6 to implement, so that the structure is more obvious, if you are not familiar with ES6 friends, please read Ruan Yifeng teacher ECMAScript 6 introduction
class myPromise {
constructor(executor){
this.value = null;
this.reason = null;
const resolve = value= >{
this.value = value; } const reject = reason= > { this.reason = reason; } try { executor(resolve,reject) } catch (e) { reject(e) } } then(onFulfilled,onRejected){ onFulfilled(this.value) onRejected(this.reason) } } Copy the code
Code parsing:
- MyPromise is a constructor that takes a higher-order function and accepts two inner functions, resolve and reject
- Any instance of myPromise can call the THEN method, which returns resolve as the first argument and Reject as the second
The first step in learning kung fu is (Since the palace) compared
Hands type
After observation, we found that the results of myPormise and Promise running we implemented were consistent (good guy!). There is only one small problem. We call P1’s THEN first, but P1’s THEN returns after P2’s THEN returns. We reserve doubt, continue to look!!
Resolve /reject singularity
This means that once resolve or reject is implemented in a promise, then resolve or reject is not implemented
And when YOU look at it, it’s not true
- The other promise returned only ‘p1-resolve’. Ours returned ‘p2-resolve2’ and ‘p2-reject1’.
- Other promise is not the same as our promise, so we still need a ‘p2-resolve1’. Resolve (‘p2-resolve1’); resolve(‘p2-resolve2’); resolve(‘p2-resolve2’); resolve(‘p2-resolve2’) This. value is reassigned by the latter. When the then function is executed, the ‘p2-resolve1’ automatically disappears.
- How do I fix it? The initial state is set as ‘pending’, and it has only one chance to change its state. It can become pending-> depressing or pending-> Rejected. Once the state is changed, it will not change again. (Doesn’t it look like pokemon evolution).
- In addition, to ensure the uniqueness of resolve/ Reject execution, resolve/ Reject is not executed when the state is not pending
So… (It’s you, Pikachu!!)
class myPromise {
constructor(executor){
+ this.state = 'pending'; // Internal states are pending,resolve,reject
this.value = null;
this.reason = null;
const resolve = value => { + if(this.state === 'pending'){ + this.state = 'fulfilled'; this.value = value; +} } const reject = reason => { + if(this.state === 'pending'){ + this.state = 'rejected'; this.reason = reason; +} } try { executor(resolve,reject) } catch (e) { reject(e) } } then(onFulfilled,onRejected){ + if(this.state === 'fulfilled'){ onFulfilled(this.value) +} + if(this.state === 'rejected') { onRejected(this.reason) +} } } Copy the code
After modification and observation, the promise of our family became normal again.
Asynchronous execution of promises
Let’s clean up our test code a little to make it look fresh and natural
After cleaning up. Let’s add asynchrony
Upon observation, we found that our promise didn’t have any print
- If resolve is not yet executed, then is already executed, and the state is ‘pending’, so there is no execution in then
- How do we deal with this situation? The first thing to think about is that when resolve/reject is executed then the state is ‘pending’, and when resolve is executed without knowing the end. So we can only temporarily save all the parameters of the THEN (onFulfilled/onRejected), and wait for the corresponding method (onFulfilled/onRejected) to be implemented after the state changes later.
- When the state changes, the corresponding method of the staging is executed immediately
Based on the above, we modify our promise
class myPromise {
constructor(executor){
this.state = 'pending'; // Internal states are pending,resolve,reject this.value = null;
this.reason = null;
+ this.onFulfilledCallbacks = []; + this.onRejectedCallbacks = []; const resolve = value =>{ if(this.state === 'pending'){ this.state = 'fulfilled'; this.value = value; + this.onFulfilledCallbacks.forEach(fn=>fn(value)) } } const reject = reason => { if(this.state === 'pending'){ this.state = 'rejected'; this.reason = reason; + this.onRejectedCallbacks.forEach(fn=>fn(reason)) } } try { executor(resolve,reject) } catch (e) { reject(e) } } then(onFulfilled,onRejected){ if(this.state === 'fulfilled'){ onFulfilled(this.value) } if(this.state === 'rejected') { onRejected(this.reason) } + if(this.state === 'pending') { + this.onFulfilledCallbacks.push(onFulfilled) + this.onRejectedCallbacks.push(onRejected) +} } } Copy the code
After that, let’s look at the resultsUpon observation, we found that our promise would normally display then callbacks, and there werebonusP1. then and p2.then are printed in the same order as when they were called.
Synchronous asynchronous consistency
Whether resolve/reject is synchronous or asynchronous in a promise, we want the execution returned by then to behave consistently. We create an execution comparison
After observation, we can find that
- A promise from someone else’s house, whether resolve is synchronous or asynchronous, then returns asynchronous
- If resolve is asynchronous, then returns asynchronous, and if resolve is synchronous, then returns synchronous
So how do you change it? Our goal is to keep the execution of THEN consistent, so we can only change all THEN to be asynchronous, for the reason explained in the previous section let’s modify our home promise
class myPromise {
constructor(executor) {
this.state = 'pending'; // Internal states are pending,resolve,reject this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = value => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onFulfilledCallbacks.forEach(fn => fn(value)) } } const reject = reason => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn(reason)) } } try { executor(resolve, reject) } catch (e) { reject(e) } } then(onFulfilled, onRejected) { if (this.state === 'fulfilled') { + setTimeout(()=>{ onFulfilled(this.value) +}) } if (this.state === 'rejected') { + setTimeout(()=>{ onRejected(this.reason) +}) } if (this.state === 'pending') { + this.onFulfilledCallbacks.push((value)=>setTimeout(()=>{onFulfilled(value)})) + this.onRejectedCallbacks.push((reason)=>setTimeout(()=>{onRejected(reason)})) } } } Copy the code
Then we’ll look at execution
Now our promise is more like someone else’s
Why arrays
Careful friend will find this. OnFulfilledCallbacks and enclosing onRejectedCallbacks is an array, in then method, when the state = = = ‘pending’, We add onFulfilled and onRejected array into the array, instead of directly assigned to this. OnFulfilledCallbacks/enclosing onRejectedCallbacks. You might think there’s nothing wrong with assigning directly.
Let’s modify our promise to store it directly in variables
class myPromise {
constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; - this.onFulfilledCallbacks.forEach((fn) => fn(value)); + this.onFulfilledCallbacks(value); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn(reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { if (this.state === "fulfilled") { setTimeout(() => { onFulfilled(this.value) }) } if (this.state === "rejected") { setTimeout(() => { onRejected(this.reason) }) } if (this.state === "pending") { - this.onFulfilledCallbacks.push((value)=>setTimeout(()=>{onFulfilled(value)})); + this.onFulfilledCallbacks = (value)=>setTimeout(()=>{onFulfilled(value)}); this.onRejectedCallbacks.push(onRejected); } } } Copy the code
After observation, we found that:
- This is a big pity. The promise of others is fulfilled normally, and the onFulfilled two THEN callbacks are normally fulfilled, while the onFulfilled one of our family is only fulfilled the second THEN callback
- The problem with our promise is that when the second THEN executes, an onledCallbacks assignment overrides the first THEN. So here only the ondepressing of the second THEN callback will be performed
Let’s roll back the code to the previous section and move on to the next section
Chain calls
The THEN method returns a Promise instance at all times
Let’s take a closer look
- A promise that executes the first THEN method succeeds in executing the second THEN method
- The promise in our family is that after executing the first THEN method, the second THEN method returns an error
- Because our then method of promise doesn’t return a promise object
Let’s revise our promise according to our analysis
class myPromise {
constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) => fn(value)); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn(reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { if (this.state === "fulfilled") { setTimeout(() => { onFulfilled(this.value) }) } if (this.state === "rejected") { setTimeout(() => { onRejected(this.reason) }) } if (this.state === "pending") { this.onFulfilledCallbacks.push((value)=>setTimeout(()=>{onFulfilled(value)})); this.onFulfilledCallbacks = (value)=>setTimeout(()=>{onFulfilled(value)}); } + return new myPromise((resolve,reject)=>{ + resolve() +}) } } Copy the code
Once the modification is complete, let’s execute it
By observation we can see that
- P2-resolve-then2 has been successfully printed, but not in the right place for someone else’s promise
The return value problem for the second THEN
The parameter to the ondepressing of the second THEN is the return value of the first onfuifle
So let’s modify our execution code
Here’s a little tip that uses the union logical operator, console.log(). This function returns undefined, so it’s not
Two flowers, one on each table,
We observed that
- The second THEN of someone else’s promise will normally receive the value passed from the first THEN
- Promis does not. It is not surprising that the resolve method of the promise returned by then has no parameters!!
Let’s change our family promise along these lines
class myPromise {
constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) => fn(value)); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn(reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { const promise2 = new myPromise((resolve, reject) => { if (this.state === "fulfilled") { setTimeout(() => { + // In case the ondepressing method fails, we will use trycatch to catch it + try { + let x = onFulfilled(this.value); + resolve(x) + } catch (error) { + reject(error) +} }) } if (this.state === "rejected") { setTimeout(() => { + try { + let x = onRejected(this.reason); + // There's a little bit of a problem here, but let's do it this way, because it's more logical + reject(x) + } catch (error) { + reject(error) +} }) } if (this.state === "pending") { this.onFulfilledCallbacks.push(value => { + try { + let x = onFulfilled(value) + resolve(x) + } catch (error) { + reject(error) +} }); this.onRejectedCallbacks.push(reason => { + try { + let x = onRejected(reason) + // There's a little bit of a problem here, but let's do it this way, because it's more logical + reject(x) + } catch (error) { + reject(error) +} }); } }) return promise2; } } Copy the code
Finally, let’s look at the results
Observation pays off, and our promises are starting to look more and more like everyone else’s.
The return of the THEN function onFulfilled will be accepted by the onFulfilled method of the next THEN function
Let’s look at an example
A closer look reveals that
- P1-reject -then1(onRejected of the first THEN)=> P1-resolve -then2(onFulfilled of the second THEN)
- P2-reject -then1 => P2-reject -then2 => P2-reject -then2 => P2-reject -then2 => P2-reject -then2
- The difference here is that the promise reject doesn’t need to be passed.
I drew a picture
So let’s revise our family promise
class myPromise {
constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) => fn(value)); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn(reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { const promise2 = new myPromise((resolve, reject) => { if (this.state === "fulfilled") { setTimeout(() => { // If ondepressing method fails, we will use trycatch to catch it try { let x = onFulfilled(this.value); resolve(x) } catch (error) { reject(error) } }) } if (this.state === "rejected") { setTimeout(() => { try { let x = onRejected(this.reason); - // There is a problem here for the moment. Let's write it this way because it is more logical - reject(x) + resolve(x) } catch (error) { reject(error) } }) } if (this.state === "pending") { this.onFulfilledCallbacks.push(value => { try { let x = onFulfilled(value) resolve(x) } catch (error) { reject(error) } }); this.onRejectedCallbacks.push(reason => { try { let x = onRejected(reason) - // There is a problem here for the moment. Let's write it this way because it is more logical - reject(x) + reslove(x) } catch (error) { reject(error) } }); } }) return promise2; } } Copy the code
That’s it. Let’s look at the results
bingo!!! Our promise is like other people’s promise
Adjust the structure
Now that the general structure is complete, we need to adjust it for the following comparison
class myPromise {
constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) => fn(value)); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn(reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { const promise2 = new myPromise((resolve, reject) => { if (this.state === "fulfilled") { setTimeout(() => { // If ondepressing method fails, we will use trycatch to catch it try { let x = onFulfilled(this.value); - reslove(x) + resolvePromise(promise,x,resolve,reject) } catch (error) { reject(error) } }) } if (this.state === "rejected") { setTimeout(() => { try { let x = onRejected(this.reason); - reslove(x) + resolvePromise(promise,x,resolve,reject) } catch (error) { reject(error) } }) } if (this.state === "pending") { this.onFulfilledCallbacks.push(value => { try { let x = onFulfilled(value) - reslove(x) + resolvePromise(promise,x,resolve,reject) } catch (error) { reject(error) } }); this.onRejectedCallbacks.push(reason => { try { let x = onRejected(reason) - reslove(x) + resolvePromise(promise,x,resolve,reject) } catch (error) { reject(error) } }); } }) return promise2; } } + function resolvePromise(promise2,x,resovle,reject) { + resolve(x) +} Copy the code
We created a function resolvePromise specifically to handle resolve logic. Since this part is highly reusable, we split it into a single function. Currently, only resolve(x) is executed in this function.
When the return value of onFulfilled/onRejected is a Promise object
Observation shows that,
- Someone else’s promise normally passes the promise’s resolve value to the second then method
- Our promise, like an iron folly, returns the promise instance as a value
Here we have to understand the promise chain call, the specific implementation logic
Resolution:
- Usually, we call resolve to pass the ondepressing of the current THEN (onFulfilled,onRejected) and the return of onFulfilled
- This is a big pity. If onFulfilled and onRejected return is a promise instance, resolve will pass the onFullfilled/onRejected return of then(onFullfilled,onRejected) of the current instance
According to our understanding, to modify our family promise
class myPromise {
constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) => fn(value)); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn(reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { const promise2 = new myPromise((resolve, reject) => { if (this.state === "fulfilled") { setTimeout(() => { // If ondepressing method fails, we will use trycatch to catch it try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) } if (this.state === "rejected") { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) } if (this.state === "pending") { this.onFulfilledCallbacks.push(value => { try { let x = onFulfilled(value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }); this.onRejectedCallbacks.push(reason => { try { let x = onRejected(reason) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }); } }) return promise2; } } function resolvePromise(promise2, x, resolve, reject) { + if (x instanceof myPromise) { + if (x.state === 'pending') { + x.then((y) => { + resolvePromise(promise2, y, resolve, reject) + }, reject) + } else { + x.then(resolve, reject) +} + } else { resolve(x) +} } Copy the code
So let’s take a look at that
After observation, I found that our family promise is becoming more and more standardized
Special cases of special cases
When resolve returns an object or function that contains the THEN function, the then function is executed once
First let’s look at the comparison
We can see from observation that,
- Someone else’s promise is a successful return, fN1 entry
- And our promise returned the whole thing
- Someone else’s promise calls fn1 and fn2, but only executes the first one because fn1 and fn2 have the same single-execution principle as resolve/ Reject
- Quick question. What if fn1/fn2 in then returns a Promise object? Think about it, right?
- The bottom line is, we’re going to have to do it again, as promised
We perfected our promise according to our ideas
class myPromise {
constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) => fn(value)); } }; const reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn(reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { const promise2 = new myPromise((resolve, reject) => { if (this.state === "fulfilled") { setTimeout(() => { // If ondepressing method fails, we will use trycatch to catch it try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) } if (this.state === "rejected") { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) } if (this.state === "pending") { this.onFulfilledCallbacks.push(value => { try { let x = onFulfilled(value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }); this.onRejectedCallbacks.push(reason => { try { let x = onRejected(reason) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }); } }) return promise2; } } function resolvePromise(promise2, x, resolve, reject) { if (x instanceof myPromise) { if (x.state === 'pending') { x.then((y) => { resolvePromise(promise2, y, resolve, reject) }, reject) } else { x.then(resolve, reject) } + } else if(x && (typeof x === 'object'|| typeof x === 'function')){ + let called = false; + try { + let then = x.then; + if(typeof then === 'function'){ + then.call(x,(y)=>{ + if(called) return; + called = true; + resolvePromise(promise2,y,resolve,reject); + },(e)=>{ + if(called) return; + called = true; + reject(e) +}) + }else{ + resolve(x) +} + } catch (error) { + if(called) return; + called = true; + reject(error) +} +} else { resolve(x) } } Copy the code
After that, let’s take a look at the results
Of course, returning a function with a THEN method is the same
You can see our family promise, once again, evolving into a promise on its own
Dealing with extremes
This is a big pity, onFulfilled, onFulfilled. Then ()
Let’s look at the comparison
Observation shows that
- Someone else’s promise was wrong
- Our family’s promise didn’t come back
- Our promise didn’t respond because it was stuck in an infinite loop of executing then
So let’s draw a picture
This is a normal situation
This ondepressing will be a self-loop when 2 returns the whole then
So here, let’s stop the self-loop
class myPromise {
. }
function resolvePromise(promise2, x, resolve, reject) {
+ if(promise2===x) {
+ Return Reject (new TypeError(' self loop ')) +} if (x instanceof myPromise) { . } } Copy the code
This is a big pity, onFulfilled,onRejected is not function
It’s a convention, so there’s no reason
Let’s add
then(onFulfilled, onRejected) {
+ onFulfilled = typeof onFulfilled === 'function'? onFulfilled:y=>y;
+ onRejected = typeof onRejected === 'function'? onRejected:y=>{throw y; }
let promise2 = new myPromise((resolve, reject) => {
Copy the code
When a promise’s reslove argument is a Promise
Temporarily do not know why, there is a big guy who knows this is also a convention, let’s take a look at an example
After observation, it can be found that the promise of resolve is the value of reslove, while the promise instance of reject is directly transmitted. Therefore, we will transform the promise of our family
const resolve = (value) => {
+ if (value instanceof myPromise) {
+ return value.then(resolve, reject)
+}
if (this.state === "pending") {
this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) => fn(value)); } }; Copy the code
Here, our family promise, something like this
class myPromise {
constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) = > { if (value instanceof myPromise) { return value.then(resolve, reject) } if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) = > fn(value)); } }; const reject = (reason) = > { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) = > fn(reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y= > y; onRejected = typeof onRejected === 'function' ? onRejected : y= > { throw y; } const promise2 = new myPromise((resolve, reject) = > { if (this.state === "fulfilled") { setTimeout((a)= > { // If ondepressing method fails, we will use trycatch to catch it try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) } if (this.state === "rejected") { setTimeout((a)= > { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) } if (this.state === "pending") { this.onFulfilledCallbacks.push(value= > { setTimeout((a)= > { try { let x = onFulfilled(value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) }); this.onRejectedCallbacks.push(reason= > { setTimeout((a)= > { try { let x = onRejected(reason) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) }); } }) return promise2; } } function resolvePromise(promise2, x, resolve, reject) { if (x === promise2) { return new TypeError('Self loop') } if (x instanceof myPromise) { if (x.state === 'pending') { x.then((y) = > { resolvePromise(promise2, y, resolve, reject) }, reject) } else { x.then(resolve, reject) } } else if (x && (typeof x === 'object' || typeof x === 'function')) { let called = false; try { let then = x.then; if (typeof then === 'function') { then.call(x, (y) => { if (called) return; called = true; resolvePromise(promise2, y, resolve, reject); }, (e) => { if (called) return; called = true; reject(e) }) } else { resolve(x) } } catch (error) { if (called) return; called = true; reject(error) } } else { resolve(x) } } Copy the code
Finally, let’s test our promise with Promises -aplus-tests
npm init
npm i promises-aplus-tests -D
Copy the code
Modify package.json
"scripts": {
// Follow your file "test": "promises-aplus-tests ./src/index.js"
},
Copy the code
perform
npm run test
Copy the code
It turned out that 40 of the tests failed
Boy, do you remember the hand that fell from the sky?
Here, other articles tell me that this is the promise. It’s like back in elementary school, when I asked my teacher a question and was told to just remember it. But, I refuse!!
Let’s take an extreme example
Why is it different?
At the root of the problem, the resolvePromise returned a fuifle promise
X.chen (reslove,reject) is executed directly within the resolvePromise() method inside the promise.
Custom ThEnable is returned as the value
So what do we do here
class myPromise {
constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (value instanceof myPromise) { return value.then(resolve, reject); } + setTimeout(() => { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) => fn(value)); } +}) }; const reject = (reason) => { + setTimeout(()=>{ if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn(reason)); } +}) }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y; onRejected = typeof onRejected === 'function' ? onRejected : y => { throw y; } const promise2 = new myPromise((resolve, reject) => { if (this.state === "fulfilled") { setTimeout(() => { // If ondepressing method fails, we will use trycatch to catch it try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) } if (this.state === "rejected") { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } }) } if (this.state === "pending") { this.onFulfilledCallbacks.push(value => { - setTimeout(() => { try { let x = onFulfilled(value) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } -}) }); this.onRejectedCallbacks.push(reason => { - setTimeout(() => { try { let x = onRejected(reason) resolvePromise(promise2, x, resolve, reject) } catch (error) { reject(error) } -}) }); } }) return promise2; } } function resolvePromise(promise2, x, resolve, reject) { if (x === promise2) { Return Reject (New TypeError(' self loop ')) } if (x instanceof myPromise) { if (x.state === 'pending') { x.then((y) => { resolvePromise(promise2, y, resolve, reject) }, reject) } else { x.then(resolve, reject) } } else if (x && (typeof x === 'object' || typeof x === 'function')) { let called = false; try { let then = x.then; if (typeof then === 'function') { then.call(x, (y) => { if (called) return; called = true; resolvePromise(promise2, y, resolve, reject); }, (e) => { if (called) return; called = true; reject(e) }) } else { resolve(x) } } catch (error) { if (called) return; called = true; reject(error) } } else { resolve(x) } } Copy the code
We’ve changed all resolve/ Reject to asynchronous firing
The last
We execute execute
npm run test
Copy the code
I’ve finally passed all the test cases
The final code
class myPromise {
constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject
this.value = null;
this.reason = null;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) = > { if (value instanceof myPromise) { return value.then(resolve, reject); } setTimeout((a)= > { if (this.state === "pending") { this.state = "fulfilled"; this.value = value; this.onFulfilledCallbacks.forEach((fn) = > fn(value)); } }); }; const reject = (reason) = > { setTimeout((a)= > { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; this.onRejectedCallbacks.forEach((fn) = > fn(reason)); } }); }; try { executor(resolve, reject); } catch (e) { reject(e); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (y) = > y; onRejected = typeof onRejected === "function" ? onRejected : (y) = > { throw y; }; const promise2 = new myPromise((resolve, reject) = > { if (this.state === "fulfilled") { setTimeout((a)= > { // If ondepressing method fails, we will use trycatch to catch it try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }); } if (this.state === "rejected") { setTimeout((a)= > { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }); } if (this.state === "pending") { this.onFulfilledCallbacks.push((value) = > { try { let x = onFulfilled(value); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }); this.onRejectedCallbacks.push((reason) = > { try { let x = onRejected(reason); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }); } }); return promise2; } } function resolvePromise(promise2, x, resolve, reject) { if (x === promise2) { return reject(new TypeError("Self cycle")); } if (x instanceof myPromise) { if (x.state === "pending") { x.then((y) = > { resolvePromise(promise2, y, resolve, reject); }, reject); } else { x.then(resolve, reject); } } else if (x && (typeof x === "object" || typeof x === "function")) { let called = false; try { let then = x.then; if (typeof then === "function") { then.call( x, (y) => { if (called) return; called = true; resolvePromise(promise2, y, resolve, reject); }, (e) => { if (called) return; called = true; reject(e); } ); } else { resolve(x); } } catch (error) { if (called) return; called = true; reject(error); } } else { resolve(x); } } myPromise.deferred = function () { let defer = {}; defer.promise = new myPromise((resolve, reject) = > { defer.resolve = resolve; defer.reject = reject; }); return defer; }; module.exports = myPromise; Copy the code
This article is formatted using MDNICE