Author: dream at dawn source: gold digging
Preface
During the interview, there are often interviewers who ask you to implement A Promise. If you implement it by referring to the A + specification, it may not end until dark.
When it comes to Promise, the core function we first think of is asynchronous chain call. This article will take you to implement a Promise that can be called asynchronously in 20 lines of code.
The implementation of this Promise does not consider any exceptions, only the shortest code, so that readers can understand the core asynchronous chain call principle.
Code
Give the code first, it's really 20 lines.
function Promise(excutor) { var self = this self.onResolvedCallback = [] function resolve(value) { setTimeout(() => { self.data = value self.onResolvedCallback.forEach(callback => callback(value)) }) } excutor(resolve.bind(self)) } Promise.prototype.then = function(onResolved) { var self = this return new Promise(resolve => { self.onResolvedCallback.push(function() { var result = onResolved(self.data) if (result instanceof Promise) { result.then(resolve) } else { resolve(result) } }) }) }
Core case
new Promise(resolve => { setTimeout(() => { resolve(1) }, 500) }) .then(res => { console.log(res) return new Promise(resolve => { setTimeout(() => { resolve(2) }, 500) }) }) .then(console.log)
This article will focus on this core case, and the performance of this code is as follows:
-
Output 1 after 500ms
-
Output 2 after 500ms
Realization
Constructor
First, implement the Promise constructor
function Promise(excutor) { var self = this self.onResolvedCallback = [] // Callback function set at Promise resolve // resolve passed to Promise handler // Hang a data directly on the instance here // Then execute the functions in the onResolvedCallback array one by one function resolve(value) { // Note that promise's then function needs to be executed asynchronously setTimeout(() => { self.data = value self.onResolvedCallback.forEach(callback => callback(value)) }) } // Execute the function passed in by the user excutor(resolve.bind(self)) }
OK, let's go back to the case
const excutor = resolve => { setTimeout(() => { resolve(1) }, 500) } new Promise(excutor)
Separately, the executor is the function passed by the user. After the resolve function is called internally, the onResolvedCallback on the promise instance will be executed once.
So far, we don't know where the function in the array of onResolvedCallback comes from, and then look down.
then
Here is the most important then implementation. Chain calls are all based on it:
Promise.prototype.then = function(onResolved) { // Save the context. The then of the promise call points to the promise. var self = this // Be sure to return a new promise // promise2 return new Promise(resolve => { self.onResolvedCallback.push(function() { var result = onResolved(self.data) if (result instanceof Promise) { // The power of resolve is given to user promise result.then(resolve) } else { resolve(result) } }) }) }
Back to the case
var excutor = resolve => { setTimeout(() => { resolve(1) }, 500) } var promise1 = new Promise(excutor) promise1.then(res => { console.log(res) // user promise return new Promise(resolve => { setTimeout(() => { resolve(2) }, 500) }) })
Note the naming here:
-
We call the instance returned by the Promise constructor Promise 1,
-
In the implementation of then, we construct a new promise return called promise 2
-
When the user calls the then method, the user manually constructs a promise for asynchronous operation, which is called user promise
In the implementation of then, self actually points to promise1
In the executor of promise2, a function is executed immediately. It push es a function into the onResolvedCallback array of promise1,
Focus on the push function. Note that this function will not be executed until promise 1 is resolve d.
self.onResolvedCallback.push(function() { // onResolved corresponds to the function passed in by then var result = onResolved(self.data) // The example returns a promise 3 if (result instanceof Promise) { // Then the resolution decision of promise 2 is directly handed over to user promise result.then(resolve) } else { resolve(result) } })
If the onResolved method passed in by the user to then returns a promise, the parameter resolve obtained in the user promise actually points to the resolve of the internal promise 2,
So this can be done: after user promise is resolve d, the N2 function will continue to execute,
new Promise(resolve => { setTimeout(() => { // resolve1 resolve(1) }, 500) }) // then1 .then(res => { console.log(res) // user promise return new Promise(resolve => { setTimeout(() => { // resolve2 resolve(2) }, 500) }) }) // then2 .then(console.log)
In fact, then1 is in the callback array of promise1, so it will not be executed until resolve1 is executed
In fact, then2 is in the callback array of promise2. As we just know, resolve2 is the resolve method of promise2,
Therefore, when resolve2 is completed, then2 will execute, which implements asynchronous chain call.
Summary of key points
One core point:
-
In a simple case, the then1 function is a synchronization function that returns a normal value. The function passed in then1 is actually put into the callback array of promise1,
// promise1 new Promise(resolve => { setTimeout(resolve, 1000) }) // then1 here the function passed in will be put into the callback array of the caller promise .then(res => { console.log(res) })
In this way, in one second, promise 1 is resolve d. Is the function in then1 executed~
-
In complex cases, the then function returns a promise. If a promise is returned in the then function, the resolve in the promise returned actually points to the
// Call promise of then new Promise(resolve => { setTimeout(resolve, 1000) }) // then2 .then(res => { // user promise return new Promise(resolve => { setTimeout(resolve, 1000) }) }) // then3 .then(res => { console.log(res) })
then2 will return promise2 (note that it is not the user promise, but the promise 2 returned from the source code),
The function passed in by then 3 will be put into the callback array of promise 2.
Because the user returns a user promise in then2,
So the resolution power of promise 2 will be transferred to user promise,
After one second, user promise is resolve d, which means promise 2 is reoslve. Then the callback function passed in by then 3 will be found in promise 2's callback array
It's perfectly executed.
Article summary
All the above codes are in
https://github.com/sl1673495/frontend-code-fragment/blob/master/promise-easy.js
In this paper, we simply implement a promise that can be called asynchronously and chain, but the real promise is much more complex than it, involving the handling of all kinds of exceptions and boundary conditions.
The promise A + specification is worth reading for every qualified front-end development.
Hope this article can help you!