Write a promise
Take everyone to hand write a promis. Before I write it by hand, I’ll briefly explain why PROMISE is used, how promise is used, and how it’s used. Because we need to understand the requirements in order to better implement them.
This article adopts the usual nanny style, feeding to your mouth. Here, Taro, take your medicine
Introduction of promise
Promise solved the problem of using callback functions in asynchronous programming, nesting layers of hell callback, hell callback is not legible, logic is not clear, believe that anyone who took over a code like this would have to curse at what was written on it. Of course there are generator and async and await methods for asynchronous programming, and async and await have some advantages over Promise. I’ll talk about it briefly at the end, but I won’t expand too much.
The callback hell
Let’s take a look at this code and see if it makes you uncomfortable, but actually writing business code is more complicated than this, like sending an Ajax request 1, which has a lot of business processing code in it, and then sending request 2, which depends on the result of request 1
let i = 0
setTimeout(() = > {
let i = 1
console.log(i)
setTimeout((i) = > {
let j = i + 2
console.log(j)
setTimeout(j= > {
let k = j + 3
console.log(k)
}, 1000, j);
}, 1000, i);
}, 1000);
Copy the code
Promise solved the problem
The above code is implemented with promise, which seems to feel like more code, but obviously the code logic is clearer, and the more nested, the more obvious the promise advantage.
let i = 0
let p = new Promise((resolve, reject) = > {
setTimeout(() = > {
i = i + 1
console.log(i)
resolve(1)},1000)
}).then(data= > {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
i = i + 2
console.log(i)
resolve(i)
}, 1000);
})
}).then(data= > {
setTimeout(() = > {
i = i + 3
console.log(i)
}, 1000);
})
Copy the code
Basic features and usage of promises
Basic features :(keep these features in mind, these are the specific requirements for implementing handwritten promises. It is normal to not understand them now, but you will understand them later in the implementation process.)
- Promise has three states: Pending, fulfilled, and rejected
- Pending can be fulfilled or rejected. Once the status changes, the status cannot be changed again
- Resolve indicates success, the status changes from Pending to depressing, and the parameter value is received
- Reject indicates failure, the status changes from Pending to depressing, and reason is received
- The Then method receives two parameters, onFulfilled and onRejected, and executes onFulfilled after pending => fulfilled, and executes onFulfilled after Pending => Fulfilled
// index.js
new Promise((resolve, reject) = > {
setTimeout(() = > {
// The asynchronous operation succeeds
resolve('hello')
// The asynchronous operation fails
// reject(' reject ')
}, 2000)
}).then(data= > {
console.log(data)
}, reason= > {
console.log(reason)
})
Copy the code
Promise to realize
The first step is to implement a basic promise
// index.js -----
let Promise = require('./promise')
let p = new Promise((resolve, reject) = > {
resolve('Success!')
// reject(' reject ~')
})
p.then(data= > {
console.log(data)
})
Copy the code
From the above code we and promise feature, let’s clarify the requirements:
- The promise returns an executor and callback method as an argument to the promise
- Executor accepts resolve and reject as Executor arguments, resolve(‘ successful ‘) accepts value, and Reject (‘ failed ~’) accepts Reason.
- Combined with the stipulation of promise, promise has three states, including pending, depressing and Rejected
- Implement a THEN method, accept two parameters as onFulfilled and onFulfilled, and execute onFulfilled after pending => fulfilled. Execute onRejected after pending => Rejected
Code implementation
-
The promise returns an executor and callback method as an argument to the promise
// promise.js class Promise { constructor(executor) { excutor() } } module.exports = Promise Copy the code
-
Executor accepts resolve and reject as Executor arguments, resolve(‘ successful ‘) accepts value, and Reject (‘ failed ~’) accepts Reason.
// promise.js class Promise { constructor(executor) { this.value = undefined this.reason = undefined const resolve = (value) = > { this.value = value } const reject = (reason) = > { this.reason = reason } excutor(resolve, reject) } } module.exports = Promise Copy the code
-
Combined with the stipulation of promise, promise has three states, including pending, depressing and Rejected
// promise.js class Promise { constructor(executor) { // Define a state, starting with pending this.state = 'pending' this.value = undefined this.reason = undefined const resolve = (value) = > { if (this.state === 'pending') { // Successful, the status changes from Pending to depressing this.state = 'fulfilled' // Received successfully ~ this.value = value } } const reject = (reason) = > { if(this.state === 'pendinng') { // Failed. The status changes from Pending to Rejected this.state = 'rejected' // Failed to receive this.reason = reason } } excutor(resolve, reject) } } module.exports = Promise Copy the code
-
Implement a THEN method, accept two parameters as onFulfilled and onFulfilled, and execute onFulfilled after pending => fulfilled. Execute onRejected after pending => Rejected
class Promise { constructor(executor) { this.state = 'pending' this.value = undefined this.reason = undefined const resolve = (value) = > { if (this.state === 'pending') { this.value = value this.state = 'fulfilled'}}const reject = (reason) = > { if (this.state === 'pending') { this.reason = reason this.state = 'rejected' } } executor(resolve, reject) } // Implement the then method then(onFulfilled, onRejected) { / / success if (this.state === 'fulfilled') { // Receive data successfully onFulfilled(this.value) } / / fail if (this.state === 'rejected') { // Failed to receive data onRejected(this.reason) } } } module.exports = Promise Copy the code
Run chestnut 1 and print successfully
The second step is to implement the asynchronous function publish and subscribe mode
Obviously chestnut 1 is a synchronous operation. If we look at Chestnut 2, we find that console.log in then does not print anything.
// index.js ------
let Promise = require('./promise')
let p = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('1s delay successful ~')},1000)
})
p.then(data= > {
console.log(data)
})
Copy the code
- Why is that?
Since all of our promises have been synchronous so far, when we execute our executor and execute the synchronization, we see an asynchronous setTimeout, queue her up (we need to know about event loops here), and immediately execute the then method.
The resolve method in setTimeout did not execute at all, the state is still pending, and the value did not get the delay of one second, so the state is still pending.
- How do you solve it?
Since then has no way of knowing when and if RESOLVE has been executed, it needs something to tell then that resolve has been executed.
Clank ~ Publish Subscriber mode, Subscriber registers the event they want to Subscribe to the dispatch center, when Publisher publishes the event to the dispatch center, that is, when the event is triggered, The processing code registered with the dispatch center by the Fire Event subscriber.
For chestnuts, for example, we go to eat haidilao, haidilao front desk has a do hand membrane welfare, I don’t know how many hands front desk little sister and membrane to do, when it was my turn, I will go aboard his public appointments, waited in line for a number, to continue to eat my haidilao, then front desk little sister finished through the information in the public, I leave, Call me from platoon number so I can get a mask.
Let’s take a look at the requirements:
While pending, we keep track of which items will wait until resolve completes, and we put them in an array. Use resolve or reject before executing them.
class Promise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
// Define an array to hold tasks to be completed later
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) = > {
if (this.state === 'pending') {
this.value = value
this.state = 'fulfilled'
/* The state changes, and the tasks we have scheduled start */
this.onResolvedCallbacks.forEach(fn= > fn())
}
}
const reject = (reason) = > {
if (this.state === 'pending') {
this.reason = reason
this.state = 'rejected'
this.onRejectedCallbacks.forEach(fn= > fn())
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
if (this.state === 'rejected') {
onRejected(this.reason)
}
if(this.state === 'pending') {
/* State is still pending because of asynchrony */
this.onResolvedCallbacks.push(() = > {
onFulfilled(this.value)
})
this.onRejectedCallbacks.push(() = > {
onRejected(this.reason)
})
}
}
}
module.exports = Promise
Copy the code
Step 3: chain call
One of the advantages of promise is that it’s called in a chain, and it’s very logical, and we can always do this chain, chestnut 3
// index.js ------
let p = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve('Delay 1 second succeeded ~')},1000)
})
p.then(data= > {
let str = data + 'I'm in the first then oh! '
console.log(str)
return str
/* return new Promise((resolve, reject) => {resolve(' resolver ')} */
})
}).then(data= > {
let str = data + 'I'm in the second then! '
console.log(str)
})
Copy the code
Requirements:
-
The promise itself contains the then method, so to implement the chain call, we can just return a promise in the THEN. Place all tasks in the THEN method into a Promise promise((resolve, reject) => {tasks in the original THEN}) and then the next THEN.
class Promise { constructor(executor) { this.state = 'pending' this.value = undefined this.reason = undefined this.onResolvedCallbacks = [] this.onRejectedCallbacks = [] const resolve = (value) = > { if (this.state === 'pending') { this.value = value this.state = 'fulfilled' this.onResolvedCallbacks.forEach(fn= > fn()) } } const reject = (reason) = > { if (this.state === 'pending') { this.reason = reason this.state = 'rejected' this.onRejectedCallbacks.forEach(fn= > fn()) } } executor(resolve, reject) } then(onFulfilled, onRejected) { // To get to the chained call then, we return a new promise instance const promise2 = new Promise((resolve, reject) = > { if (this.state === 'fulfilled') { This is a big pity. /* The result of ondepressing is the result of the callback function in March 3, and then the next method */ will be implemented immediately resolve(onFulfilled(this.value)) } if (this.state === 'rejected') { reject(onRejected(this.reason)) } if(this.state === 'pending') { this.onResolvedCallbacks.push(() = > { resolve(onFulfilled(this.value)) }) this.onRejectedCallbacks.push(() = > { reject(onRejected(this.reason)) }) } }) return promise2 } } module.exports = Promise Copy the code
-
The Then method may return an ordinary value, or it may return a promise. If it’s a normal value, we pass it directly to value, but if it’s a promise, we also implement the promise’s resolve or reject.
// index.js -- then can return a normal value, or it can return a promise let Promise = require('./promise') let p = new Promise((resolve, reject) = > { setTimeout(() = > { resolve('Delay 1 second succeeded ~')},1000) }) p.then(data= > { let str = data + 'I'm in the first then.' console.log(str) return new Promise((resolve, reject) = > { resolve(str) }) }).then(data= > { let str = data + 'I'm in the second then.' console.log(str) }) Copy the code
// promise.js class Promise { constructor(executor) { this.state = 'pending' this.value = undefined this.reason = undefined this.onResolvedCallbacks = [] this.onRejectedCallbacks = [] const resolve = (value) = > { if (this.state === 'pending') { this.value = value this.state = 'fulfilled' this.onResolvedCallbacks.forEach(fn= > fn()) } } const reject = (reason) = > { if (this.state === 'pending') { this.reason = reason this.state = 'rejected' this.onRejectedCallbacks.forEach(fn= > fn()) } } executor(resolve, reject) } then(onFulfilled, onRejected) { const promise2 = new Promise((resolve, reject) = > { if (this.state === 'fulfilled') { /* Since this is not the case, we use setTimeout to execute the contents in the next event loop where the */ has already been generated setTimeout(() = > { let x = onFulfilled(this.value) resolvePromise(promise2, x, resolve, reject) }, 0)}if (this.state === 'rejected') { setTimeout(() = > { let x = onRejected(this.reason) resolvePromise(promise2, x, resolve, reject) }, 0)}if(this.state === 'pending') { this.onResolvedCallbacks.push(() = > { setTimeout(() = > { let x = onFulfilled(this.value) resolvePromise(promise2, x, resolve, reject) }, 1000)})this.onRejectedCallbacks.push(() = > { setTimeout(() = > { let x = onRejected(this.reason) resolvePromise(promise2, x, resolve, reject) }, 0)})}})return promise2 } } function resolvePromise(promise, x, resolve, reject) { if(typeof x === 'function'| | -typeof x === 'object'&& x ! = =null)) { try { const then = x.then if (typeof then === 'function') { // If an x is a promise, then two callbacks are executed, either successfully or unsuccessfully then.call(x, y= > { resolve(y) }, r= > { reject(r) }) } } catch (err) { reject(err) } } else { // Just a normal value resolve(x) } } module.exports = Promise Copy the code
So let’s do that
-
What if we return promises with promises? Recursive!
// index.js ----- let Promise = require('./promise') let p = new Promise((resolve, reject) = > { setTimeout(() = > { resolve('Delay 1 second succeeded ~')},1000) }) p.then(data= > { let str = data + 'I'm in the first then.' console.log(str) // The return value is promise package Promise return new Promise((resolve, reject) = > { resolve(str + new Promise((resolve, reject) = > { resolve(data + 'promise the promise') console.log(str + 'promise the promise') })) }) }).then(data= > { let str = data + 'I'm in the second then.' console.log(str) }) Copy the code
// promise.js.function resolvePromise(promise, x, resolve, reject) { if(typeof x === 'function'| | -typeof x === 'object'&& x ! = =null)) { try { const then = x.then if (typeof then === 'function') { then.call(x, y= > { / / recursion resolvePromise(promise, y, resolve, reject) }, r= > { reject(r) }) } } catch (err) { reject(err) } } else { resolve(x) } } .... Copy the code
I think it is almost the same, promise of course, there are all and other methods, next time I have time to more, today I am tired, do not want to write.
Reference Documents:
promise
Write a promise
The most detailed handwritten Promise tutorial ever