This implementation is implemented according to the promiseA+ specification. When using a promise, we need to create a new instance, so we need to construct a promise constructor

let p = new Promise(function(resolve, reject){// Take the first one, reject(That's not true.); / / resolve the failure (1); // success // if an exception is thrown, it will also go tothenThrow new Error('wrong'); 
});
p.then(function(data) {
    console.log(data);    
}, function(err) {
    console.log(err);      // That's not true.
});
Copy the code

The Promise instance of new contains an executor that takes two parameters, a successful and a failed method

The important thing to remember here is that successful and failed methods are mutually exclusive, and when called simultaneously, the first called method is executed first, and the second called method is not executed again

state

We all know that promises have three states

  • Pending Indicates the default status
  • Fulfilled (successful)
  • Rejected (failure)

Maybe this is a big pity or Rejected. Once the constructor is fulfilled, the status will not change. Maybe it will be successful or failed

/ / to realize the promisefunction Promise(executor) {
	const self = this;
	self.status = 'pending'; // Default state,pending-> depressing,pending-> Rejected self.value = null; // Success value self.reason = null; // Cause of failurefunctionResolve (value) {// Successif (self.status === 'pending') {
		self.status = 'fulfilled'; self.value = value; }}functionReject (reason) {// Failif (self.status === 'pending') {
		self.status = 'rejected'; self.reason = reason; } } try { executor(resolve, reject); } catch(e) {reject(e) {reject(e); } } Promise.prototype.then =function(onFulfilled, onRejected) {
	const self = this;
	if (self.status === 'fulfilled') {
	    onFulfilled();
	}
	if (self.status === 'rejected') { onRejected(); }}; module.exports = Promise;Copy the code

It says a lot, but it turns out it’s dealing with synchronization, so what do we do when we have asynchrony, so let’s keep going

Asynchronous promise

Asynchronous code can be written in promises, and multiple then’s can be made

let p = new Promise(function(resolve, reject){// Asynchronous operationsetTimeout(function() {
        resolve('ok');
    }, 1000);
}).then(function(data) {
    console.log(data);    // 'ok'
    return'The person below then +${data}`;
}, function(err) {
    console.log(err);     
}).then(function(data) {
    console.log(data);    // 'Ok from the bottom.'
}, function(err) {
    console.log(err);     
});
Copy the code
Make an asynchronous promise happen

The promise instance can be repeated then, and the successful methods in then will be executed successively after success. We can first store the successful callback and failed callback in then into the array, and call the successful array when success occurs

function Promise() {
    self.status = 'pending'; self.value = null; self.reason = null; + self.onFulfilledCb = []; / / storethenSuccessful callback + self.onrejectedCb = []; / / storethenFailed callbackfunction resolve(value) {
        if (self.status === 'pending') {
            self.status = 'fulfilled';
            self.value = value;
     +      self.onFulfilledCb.forEach(function(fn) { fn(); }); }}function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
      +     self.onRejectedCb.forEach(function(fn) { fn(); }); } } try { executor(resolve, reject); } catch(e) {reject(e) {reject(e); } } Promise.prototype.then =function(onFulfilled, onRejected) {
    const self = this;
    if (self.status === 'fulfilled') {
        onFulfilled(self.value);
    } 
    if (self.status === 'rejected') { onRejected(self.reason); } // when calledthenWhen, it may be in a pending state + with no success or failureif (self.status === 'pending') {// Add the successful callback to the array self.ononledcb.push (function () {
            onFulfilled(self.value);
        });
        self.onRejectedCb.push(function() { onRejected(self.reason); }); }}; module.exports = Promise;Copy the code

Chain calls

The above code implements how the then method is handled in asynchronous calls,

Now comes the important part, as we all know that promise’s then method also supports chained calls

But the chained call to THEN is different from the way jquery returns this

☆️ it returns a new promise

So let’s look at the code

let p = new Promise(function(resolve, reject) {
        resolve('ok');
});
p.then(function (data) {
    console.log(data);
    return data;
}, function (err) {
    console.log(err);
    // return 'Successful state if there is a return value'
}).then(function (data) {
    console.log('2'+data);
}, function (err) {
    console.log(err);
})
Copy the code

Now let’s implement the chained call, as usual, with the + sign representing the main code:

Promise.prototype.then = function (onFulfilled, onRejected) {
    const self = this;
 +  letpromise2; // Define a promise2 variableif (self.status === 'fulfilled'// return a new promise + promise2 = new promise (function(resolve, reject) {// x can be a normal value or a promise +letx = onFulfilled(self.value); + resolvePromise(promise2, x, resolve, reject) + resolvePromise(promise2, x, resolve, reject) }); }if (self.status === 'rejected') {
 +       promise2 = new Promise(function (resolve, reject) {
 +          letx = onRejected(self.reason); + resolvePromise(promise2, x, resolve, reject); }); } // when calledthenIt may be in a pending state with no success or failureif (self.status === 'pending') {// Add successful callback to array + promise2 = new Promise(function (resolve, reject) {
            self.onFulfilledCb.push(function() {+let x = onFulfilled(self.value);
+               resolvePromise(promise2, x, resolve, reject);
            });

            self.onRejectedCb.push(function() {+let x = onRejected(self.reason);
+               resolvePromise(promise2, x, resolve, reject);
            });
        });
    }

    return promise2;
};
function resolvePromise(p2, x, resolve, reject) {
    if(p2 === x) {// Cannot return itselfreturn reject(new TypeError('Circular reference'));
    }
    letcalled; // x returns either an object or a function or a normal valueif(x ! == null && (typeof x ==='object' || typeof x === 'function')) {
        try {
            let then = x.then;

            if (typeof then= = ='function') {
                then.call(x, function(y) {// Prevent multiple callsif (called) return;
                    called = true; // y may still be a promise, so the recursion continues until a normal resolvePromise(p2, y, resolve, reject) is returned; },function (e) {
                    if (called) return;
                    called = true;
                    reject(e);
                });
            } else{/ / processingthenIs not a function, such as {thenResolve (x); } } catch(e) {if (called) return;
            called = true; reject(e); }}else{ resolve(x); // Return a normal value}}Copy the code

Some conclusions can also be drawn from the above code

  • If either a successful callback or a failed callback has a return value, the success of the next THEN is followed
  • If the first promise returns a normal value, the next successful callback for then is entered; If the first promise returns a new promise, wait for the result of the return promise execution to be passed to the next THEN
  • The resolvePromise function returns the same result as the promise, neither success nor failure

So we’ve already implemented the chain call for promise, but that’s not enough. So there’s a case where a promise keeps making the chain call when nothing is passed in then, right

let p = new Promise(function (resolve, reject) {
    reject(999);
});
p.then().then().then(function (data) {
    console.log(data);  // 999    
}, function (err) {
    console.log(err);
});
Copy the code

This is the penetration of the promise median, and the implementation is in then, so let’s modify the THEN method to implement that as well

Promise.prototype.then = function(onFulfilled, onRejected) {
+    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value
    };
+    onRejected = typeof onRejected === 'function' ? onFulfilled : function (err) {
        throw err
    };  
    const self = this;
    let promise2;
};
Copy the code

It’s very simple, if you don’t give me a callback function in then, and I’m just going to check that there is no callback, and I’m going to create a new function for you and pass it to the next THEN when the callback succeeds

In addition, according to the Promise specification, all the onFulfilled and onRejected need to be performed asynchronously, so we need to add asyncio to them. Obviously, this is easier to implement, see the code below

Promise.prototype.then = function(onFulfilled, onRejected) {
    if (self.status === 'fulfilled') {
        promise2 = new Promise(function(resolve, reject) {// If an exception occurs on a successful or failed execution, the returned promise should be in the failed state // try/catch is used to prevent the exception from being returned // plussetTimeout implements asynchronous calls +setTimeout(function () {
                try {
                    letx = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); }}); }); }if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
+           setTimeout(function () {
                try {
                    letx = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); }}); }); }};if (self.status === 'pending') {// Add successful callback to array promise2 = new Promise(function (resolve, reject) {
            self.onFulfilledCb.push(function() {+setTimeout(function () {
                    try {
                        letx = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); }}); }); self.onRejectedCb.push(function() {+setTimeout(function () {
                    try {
                        letx = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject); } catch(e) { reject(e); }}); }); }return promise2;
Copy the code

Ok, so we’ve implemented the most important then method of promise.

Let’s write a few more then words. Let’s write a few more then words.

Other methods

Catch method for catching errors

The onRejected callback is the same as the then callback onRejected

let p = new Promise(function(resolve, reject) {
    reject('No, no, no, no.');
});
p.then(function(data){
    console.log(data);
}).catch(function(e){
    console.log(e);    // 'No, no, no, no.'
});
Copy the code

Here’s the code:

// Method to catch errors promise.prototype. catch =function(callback) {// also a callthenMethod to pass a NULL to a successful callback and a callback to a failed callbackreturn this.then(null, callback); 
};
Copy the code

The Promise class also has some methods of its own, commonly used are All, Race, resolve, and Reject

Have written here, then continue to write, nonsense not to say, open masturbation!!

All methods

The use of the Promise. All ()

let p = new Promise(function(resolve, reject) {
   setTimeout(function() {
        resolve('Beijing');
    }, 1000);
});
let p2 = new Promise(function(resolve, reject) {
    setTimeout(function() {
        resolve('nanjing');
    }, 200);
});
let p3 = new Promise(function(resolve, reject) {
    resolve('Tokyo');
});
Promise.all([p, p2, p3]).then(function(data) {// All will be executed in the same order as in all. The timer in p is only the time when the execution is completed. Log (data); log(data); log(data); / / /'Beijing'.'nanjing'.'Tokyo']});Copy the code

Know the usage above, write the realization below, guest officer and stay to continue to see

Promise.all = function(items) {
    return new Promise(function(resolve, reject) {
        letres = []; // Used to store the value returned by the success functionletnum = 0; // Records all return a success numberletlen = items.length; // The length of the array // Traverses the arrayfor (let i = 0; i < len; i++) {
              items[i].then(function(data) {
                  res[i] = data;
                  if(++num === len) { resolve(res); } }, reject); }}); };Copy the code

And so on, and so on, and so on

Keep up the good work and the Race approach

So race method just as the name implies, race is a competition, that is, who is faster than the first to succeed, the return is whose success data

Race method

Let’s see how it works. Okay

let p = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('Beijing');
    }, 1000);
});
let p2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('nanjing');
    }, 500);
});
let p3 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('Tokyo');
    });
});
Promise.race([p, p2, p3]).then(functionConsole. log(data) {// log(data); // log(data); // log(data); // log(data); //'Tokyo'
});
Copy the code

So, without further ado, write

Promise.race = function(items) {
    return new Promise(function(resolve, reject) {
        for (leti = 0; i < items.length; i++) { items[i].then(resolve, reject); }}); };Copy the code

That implements the race method, so write resolve and Reject

So these two methods seem pretty simple, because we’ve already done a pretty good job of preparing them for direct use but let’s see how they work

Resolve and Reject methods
/ / Promise success. Resolve ([1, 2, 3, 4]). Then (function(data) { console.log(data); / / [1, 2, 3, 4]}); / / fail Promise. Reject ('err! ').then(function () {}, function (err) {
    console.log(err);   // 'err! '
});
Copy the code

Come on, come on. Write. Don’t hesitate

Promise.resolve = function(value) {
    return new Promise(function(resolve, reject) {
        resolve(value);
    });
};
Promise.reject = function (reason) {
    return new Promise(function (resolve, reject) {
        reject(reason);
    });
};
Copy the code

Done!!

Ladies and gentlemen, do you feel that your body has been hollowed out? After writing so much, you can comb it. No, there is one last method left, which is defer

What does the defer method do?

First of all, it doesn’t have to write new to generate a promise instance,

The second is because promise’s library promises- aplus-Tests needs to write a method like this

Let’s see how it works. It’s the same thing as Q, right

function read() {
    letdefer = Promise.defer(); Q.doer () require(q.doer () require('fs').readFile('./2.promise/1.txt'.'utf8'.function (err, data) {
        if(err) defer.reject(err);
        defer.resolve(data);
    })
    return defer.promise;
}
read().then(function (data) {
    console.log(data)
},function(err){
    console.log(err);
})
Copy the code

Implement the last one for the sake of testing your library

Promise.defer = Promise.deferred = function() {
    let def = {};
    def.promise = new Promise(function(resolve, reject) {
        def.resolve = resolve;
        def.reject = reject;
    });

    return def;
}
Copy the code

Finally, all the implementations are complete. It is not easy, and thank you for your persistence, hope to gain! Ha ha

  • Finally, when each guest officer is also trying to realize the promise
  • Don’t forget to test your finished code
  • The promiseA+ specification also has a library of tests to implement promises
    • Promises – Aplus – tests-g
    • Promises – aplus – tests project. Js
  • If running is a square root, congratulations, you have one more skill