ES6 version Promise implementation, give you a different experience
Abstract: A long, long time ago, Promise did not come into this world. There were siege lions in the forest at that time, and they suffered the ravages of callback hell (callback Pyramid). Until one day, an anonymous agent, code-named Promise, rescued them from callback hell. Since then, many people have gone on the road to find Promise, and so have I…
Friendship reminder: this article uses ES6 to realize the Promise, not children shoes please imagine! What? This classmate you unexpectedly do not know ES6, good, after school please do not go, we separate exchange……
Call back to Hell VS Promise
Take fs (node’s core package) for example. Let’s say we need to request data from both A.tuck and B.tuck, and then operate on that data. This demand is often encountered in our development oh!
- Used to be callback hell
let fs = require('fs');
let arr = [];
fs.readFile('a.txt'.'utf8'.function(err,data){
arr.push(data);
fs.readFile('b.txt'.'utf8'.function(err,data){ arr.push(data); Console. log(arr); console.log(arr); })})Copy the code
- Now the Promise of
let fs = require('fs');
function read(url,coding){// First we promise fs.readfile ()return new Promise((resolve,reject)=>{
fs.readFile(url,coding,function(err,data){
if(err) reject(err);
resolve(data);
})
})
}
Promise.all([read('a.txt'.'utf8'),read('b.txt'.'utf8'). Then (data=>{// We can directly manipulate the data of the requested two files,Promise also helpfully returns an array console.log(data); })Copy the code
Promise’s war with Callback Hell, by contrast, wasn’t even on the same level from the start. Callback Hell sounded powerful, but it wasn’t. Promise’s heart should be like this:
Promises come true on their own
At this point, I’m sure you’re wondering what the core implementation of Promise is. Next, please don’t close your eyes, look here, look here! I’ll tell you how MY path to finding Promise went dark. (From this title, laughing out of pigs)
1. Promise class encapsulation
At first, I found out that Promise could be new, indicating that Promise came from a class, which was a valuable clue. (Everybody knows that, you’re telling me)
Class Promise {constructor(executor) {// Executor is the new Promise parameter this.status ='pending'; // Save state this.reason = undefined; This. value = undefined; // Successful resultslet resolve = (value)=> {
if(this.status === 'pending'){
this.status = 'resolved'; this.value = value; }}let reject = (reason)=>{
if(this.status === 'pending'){
this.status = 'rejected'; this.reason = reason; } } try { executor(resolve, reject); } catch (e) {reject(e); }} // Define on the instancethenMethod,thenAll calls are made asynchronouslythen(onFulfilled, onRejected) {
if(this.status === 'resolved'Onprogressively onprogressively (this.value); // Perform onprogressively onprogressively (this.value); }if(this.status === 'rejected'){// This is a big pity (this. Reason); }}}Copy the code
How could this be written out with only one clue? It’s so confusing! Don’t worry, listen to me slowly:
executor
Executor execution is synchronous. Why try executor execution when the default is newthrow new Error('error')
“, go rejectresolve, reject
To define theexecutor
theresolve
Successful callback function andreject
Failed callback function with two argumentsreason,value
: represents the successful return value and failure cause respectivelystatus
: save thePromise
The three states ofpending
(waiting state),fulfilled
(Success state),rejected
(Failed state)- When a
promise
Be in the state ofpending
Phi, it can transition to phifulfilled
orejected
- When a promise state is in
fulfilled
Or when therejected
Cannot transition to any other state
- When a
then
Function:Promise
It can be called by chainthen
The function is defined inPromise
The prototype of the classPrototype
On the.
Now that we’ve managed to deal with promises in sync, don’t you think you’ve tracked down the ultimate Promise power? (Smoke a cigarette, calm down the restless mood)
2. Asynchronous implementation of Promise
The Promise executor executes synchronously, and it doesn’t wait for us to do anything about it. Based on the above code, we add the following lines:
class Promise { constructor(executor) { this.onResolvedCallbacks = []; // Save the successful callback this.onRejectedCallbacks = []; // Save the failed callbacklet resolve = (value)=> {
if(this.status === 'pending'){
this.status = 'resolved'; this.value = value; this.onResolvedCallbacks.forEach(fn=>fn()); }}let reject = (reason)=>{
if(this.status === 'pending'){
this.status = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach(fn=>fn()); }}}then(onFulfilled, onRejected) {
if(this.status === 'pending'){ this.onResolvedCallbacks.push(()=>{ onFulfilled(this.value); }); this.onRejectedCallbacks.push(()=>{ onRejected(this.reason); }); }); }}}Copy the code
When asynchronous code occurs, the status of status is still pending. We can store the successful and failed callbacks of then function first. After the asynchronous code is executed, the status of status changes, and then execute the saved callbacks in sequence.
If you feel that you have basically mastered the Promise implementation, you can only say that you know nothing about the core power of Promise. (Cut the crap and write) All right, everyone!
3. Implementation of the chain call of Promise
Before we start, let’s take a look at the following code:
// The Promise here is wrapped in ES6, not implemented by ourselveslet promise = new Promise((resolve,reject)=>{
resolve('123');
})
let promise2 = promise.then((data)=>{
throw new Error('error'); }) promise2.then((data)=>{ console.log(data); },(err)=>{ console.log(err); // error})Copy the code
The return value is not this, because promises cannot be called successfully and cannot fail again. Therefore, the return value of promise2 after the then function is executed is a new promise instance. (Not like jQuery’s chain call)
Promise’s constructor code doesn’t need to change, just rewrap the THEN function:
thenThis is a big pity, onRejected) {// onFulfilled and onRejected may not be fulfilled = typeof onFulfilled === = this is a big pity'function' ? onFulfilled : value=>value;
onRejected = typeof onRejected === 'function' ? onRejected : (err)=>{throw err};
letpromise2; // Each call is requiredthenPromise2 = new promise ((resolve, reject)=>{if(this.status === 'resolved') {setTimeout(()=>{
try {
letx = onFulfilled(this.value); ResolvePromise (promise2,x,resolve, reject); } catch (e) { reject(e); }}}, 0)if(this.status === 'rejected') {setTimeout(()=>{
try {
letx = onRejected(this.reason); resolvePromise(promise2,x,resolve, reject); } catch (e) { reject(e); }}}, 0)if(this.status === 'pending'){
this.onResolvedCallbacks.push(()=>{
setTimeout(()=>{
try {
letx = onFulfilled(this.value); resolvePromise(promise2,x,resolve, reject); } catch (e) { reject(e); }}}, 0)); this.onRejectedCallbacks.push(()=>{setTimeout(()=>{
try {
letx = onRejected(this.reason); resolvePromise(promise2,x,resolve, reject); } catch (e) { reject(e); }}}, 0)); }})return promise2;
}
Copy the code
onFulfilled,onRejected
: What needs to be done when there is no transmissionpromise2
:then
The return value of the function is a new PromisesetTimeout
: Promise/A+specification) requirementsthen
The function must be asynchronous, and of course the native Promise implementation is not a setTimeout, but a microtaskresolvePromise
: encapsulationresolvePromise
Method, when a success or failure function in the then function returns a value x that may still be a promise
The resolvePromise method defined:
let resolvePromise = (promise2,x,resolve, reject)=>{
letcalled; // promise2 returns the same value as the functionif(promise2 === x){
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}
if(x! ==null && (typeof x ==='object' || typeof x === 'function')){
try {
let then = x.then;
if(typeof then= = ='function'){
then.call(x,(y)=>{
if(called) return;
called = true; resolvePromise(promise2,y,resolve, reject); // Recurse until y is a normal value},(err)=>{if(called) return;
called = true; reject(err); })}else{ // thenIf phi is a constantif(called) return;
called = true;
resolve(x);
}
} catch (e) {
if(called) return;
called = true; reject(e); }}else{// if x is a constantif(called) return;
called = true; resolve(x); }}Copy the code
- Four parameters:
promise2
(The return value of the then function is a new Promise)x
(return value of successful and failed functions in then)resolve
(Resolve of promise2)reject
(Promise2 reject) called
Increased:called
Judge, prevent multiple calls, because the logic here is not only yours, but somebody else’s, somebody else’spromise
The call may fail as well as succeedlet then = x.then
:x
Maybe it’s still apromise
So let’s do thisPromise
perform
This brings us to the core power of promise. Come, let’s have a little celebration:
3, Promise and so on method implementation
Of course, we have already begun to understand the core strength of Promise. In our development process, in addition to the THEN method, we will also use some of its other commonly used methods, just like a seasoned secret agent, you can use a knife, but also a gun. We define them on the Promise class:
static resolve(value){
return new Promise((resolve,reject)=>{
resolve(value);
})
}
static reject(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
static all(promises){
return new Promise((resolve,reject)=>{
let arr = [];
let i = 0;
let processData = (index,data)=>{
arr[index] = data;
if(++i === promises.length){ resolve(arr); }}for(let i = 0; i< promises.length; i++){
promises[i].then(data=>{
processData(i,data);
},reject);
}
})
}
static race(promises){
return new Promise((resolve,reject)=>{
for(let i = 0; i< promises.length; i++){
promises[i].then(resolve,reject);
}
})
}
catch(onRejected){
return this.then(null,onRejected);
}
Copy the code
Resolve, Reject, All, and Race are all familiar with the catch prototype, so I won’t go into any more details.
Because, I really can’t make this up, I have more important things to do:
Conclusion: spent a long time to write this article, if this article makes you more or less some harvest, please don’t be stingy with your praise (point a praise before leaving, little brother little sister), if there are wrong places to write, also hope you can not stingy advice, thank you very much! Original article, reproduced please indicate the source!