Introduction of Promise

(this part since the blog.csdn.net/u010576399/…). The Promise object is a specification proposed by the CommonJS working group to provide a unified interface for asynchronous operations.

Promises are Promises.

First, it’s an object, which means it’s used just like any other JavaScript object. Second, it acts as a proxy, acting as an intermediary between asynchronous operations and callback functions. It enables asynchronous operations to have synchronous operations interface, so that the program has a normal synchronous running process, callback functions do not need to be nested layer by layer.

In simple terms, the idea is that each asynchronous task returns a Promise object immediately, and since it returns immediately, you can follow the flow of synchronous operations. Promises object has a THEN method that allows callbacks to be specified and called after asynchronous tasks are completed.

For example, the asynchronous operation f1 returns a Promise object whose callback f2 is written as follows:

(new Promise(f1)).then(f2);
Copy the code

This is especially convenient for multi-layer nested callback functions.

// the traditional script is step1(function (value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) { // ... }); }); }); }); // Promise(new Promise(step1)). Then (step2). Then (step3). Then (step4);Copy the code

Promises Interface As you can see from the code above, Promises interface makes the flow very clear and easy to read.

Note that the generation format of the Promise object in the code above has been simplified for ease of understanding.

In general, the traditional way of writing callback functions makes the code get mixed up and move sideways rather than downwards. Promises are a solution to this problem. The promise specification aims to use normal procedural flow (synchronous) to handle asynchronous operations. It returns a Promise object to which subsequent operations are sent synchronously. Wait until the asynchronous operation has a result before performing any other operations previously placed on it.

Promises were originally an idea from the community. Some external libraries were the first to make it happen. ECMAScript 6 writes them into the language standard, so the JavaScript language currently supports Promise objects natively.

Manually implement a Promise that satisfies Promises – aplus-Tests

Promisesaplus.com/ is a website that introduces how promises come true. According to the information provided on the website, we can try to write a promise ourselves and test it using promises- aplus-Tests.

Step1

The basic promise functionality is implemented first: the code in the executor is executed after the promise is created, and the corresponding function is executed during then. It is important to note that when an error is thrown in the actuator, you should catch the error and execute reject directly

Resolve () is called in the executorthenOnly onFulfiled method is executed in, and reject() is called in the actuatorthenIn, only the onRejected method is executed. If both methods are used, the onRejected method is executed firstthenNo impact.functionPromise (executor) {// executorlet self = this;
    self.status = 'pending'; Self. value = undefined; self.value = undefined; // Default self.reason = undefined;function resolve(data_value) {
        if(self.status === 'pending') {
            self.status = 'resolved'; self.value = data_value; }}function reject(data_reason) {
        if(self.status === 'pending') {
            self.status = 'rejected'; self.reason = data_reason; }} // Execute a try catch (reject) {execute (resolve, reject); } catch (e) { reject(e); Reject}} promise.prototype.then =function (onFulfiled, onRejected) {
    let self = this;
    if(self.status === 'resolved') {
        onFulfiled(self.value);
    }
    if(self.status === 'rejected') { onRejected(self.value); }}Copy the code

step2

The problem is that if there is asynchronous code in the executor, the above implementation will be problematic because it is executing

executor(resolve, reject);   
Copy the code

There is no mechanism in then to determine whether asynchronous code has been executed. Our solution is: If the state is pending(during the execution of the constructor New Promise()), onFulfiled, OnRejected is first queued (implemented with array) and executes these methods when the state changes.

functionPromise (executor) {// executorlet self = this;
    self.status = 'pending'; self.value = undefined; // Default self.reason = undefined; self.onResolvedCallbacks = []; / / storethenSuccessful callback array self.onRejectedCallbacks = []; / / storethenFailed callback arrayfunction resolve(data_value) {
        if(self.status === 'pending') {
            self.status = 'resolved';
            self.value = data_value;
            self.onResolvedCallbacks.forEach(function(fn) {// The onRejectedCallbacks function fn() is executed when resolve is called; }}})function reject(data_reason) {
        if(self.status === 'pending') {
            self.status = 'rejected';
            self.reason = data_reason;
            self.onRejectedCallbacks.forEach(function(fn) {// The onRejectedCallbacks function fn() is executed when resolve is called; }) } } try { executor(resolve, reject); } Catch (e) {reject(e);} catch (e) {reject(e);} catch (e) {reject(e); } } Promise.prototype.then =function (onFulfiled, onRejected) {
    let self = this;
    if(self.status === 'resolved') {
        onFulfiled(self.value);
    }
    if(self.status === 'rejected') { onRejected(self.reason); } // when calledthenIt may not succeed or failif(self.status === 'pending') {/ / at this point no resolve no reject self. OnResolvedCallbacks. Push (functionOnFulfiled (self.value) {return filed(self.value); }); self.onRejectedCallbacks.push(function(){ onRejected(self.reason); }); }}Copy the code

step3

So far, we have only completed the most basic functionality of Promise, but an important feature of Promise: the chained invocation of THEN has yet to be implemented. Jquery returns this as a new promise. Let’s create a new variable promise2 in then

self.status === 'resolved'
Copy the code

For example, change the previous code to:

if(self.status === 'resolved') {
        promise2 = new Promise(function(resolve, reject){// make the previous onethenThe executor of the new Promise gets promise2 as the next timethenReturn value onFulfiled(self.value); // The return promise execuator executes onFulfiled instead of resolve or reject.})}Copy the code

Promise2 is created without a resolve/ Reject rule, so it’s a work-in-progress. If onFulfile/onRejected returns a value, then pass it as a resolve/reject parameter. Then will have a state. According to the different returned values, the returned values are respectively treated as ordinary values and promise values. Resolve () is called resolvePromise(promise2, x, resolve, Reject), where x is the return value of onFulfiled. The resolvePromise code is as follows:

functionResolvePromise (promise2, x, resolve, reject) {// Allow others to write as much as possibleif(promise2 === x) {// There should be a type error for circular referencesreturn reject(new TypeError('Circular reference')); } // If x is a promise, promise should be an objectif(x! == null && (typeof x ==='object' ||typeof x === 'function') {// It could be a promise to see if there is one in this objectthenIf there is a try catch, let's say it's a promise. Try {let then = x.then;
            if (typeof then= = ='function') {
                then.call(x, function(y) {// resolvePromise(promise2, y, resolve, reject)},function(err) {// Reject (err); })}else{resolve(x) // ifthenIf not, return x.}} Catch (e) {reject(e)}}else{// Normal valuereturn resolve(x)
    }
}
Copy the code

The corresponding

self.status === 'resolved'
Copy the code

The code of is:

if(self.status === 'resolved') {
        promise2 = new Promise(function(resolve, reject){  
            letx = onFulfiled(self.value); resolvePromise(promise2, x, resolve, reject); })}Copy the code

For example, to ensure the universality of the code, we should determine the returned promise in the code: if both promises are called, which promise will be called first and which promise will be ignored. A variable called is introduced for this purpose.

functionResolvePromise (promise2, x, resolve, reject) {// Allow others to write as much as possibleif(promise2 === x) {// There should be a type error for circular referencesreturn reject(new TypeError('Circular reference')); } // If x is a promise, promise should be an objectletcalled; // indicates whether the call succeeded or failedif(x! == null && (typeof x ==='object' ||typeof x === 'function') {// It could be a promise to see if there is one in this objectthenIf there is a try catch, let's say it's a promise. Try {let then = x.then;
            if (typeof then= = ='function') {// success then.call(x,function(y) {
                    if (called) return
                    called = true;
                    resolvePromise(promise2, y, resolve, reject)
                }, function(err) {
                    if (called) return
                    called = true; reject(err); })}else{resolve(x) // ifthenIf not, return x.}} Catch (e) {if (called) return
            called = true;
            reject(e)
        }
        
    } else{// Normal valuereturn resolve(x)
    }
}
Copy the code

Step4

So far, the big picture of promise is almost complete. The next two minor issues that need to be resolved are value penetration and the asynchronous execution of onFulfiled/onRejected. Value penetration means that data in onFulfiled() is automatically returned when there are no methods used in then. It is also easy to implement. Make a judgment at the very beginning of the definition of then:

Promise.prototype.then = function(onFulfiled, onRejected) {// Success and failure are not transmitted by default, and a default function can achieve the value of the through.onFulfiled = typeof onFulfiled ==='function'? onFulfiled:function(value) {
        return value;
    }
    onRejected = typeof onRejected === 'function'? onRejected:function(err) { throw err; // Should go next in case of value penetrationthenOnRejected instead of onFulfiled}..... }Copy the code

Onond /onRejected asynchronously executes onRejected

if(self.status === 'resolved') {
    promise2 = new Promise(function(resolve, reject){
    letx = onFulfiled(self.value); resolvePromise(promise2, x, resolve, reject) ... }}Copy the code

The code block uses setTimeout. The side effect of this is the code block in the Promise constructor

try {
        executor(resolve, reject);   
    } catch (e) {  
        reject(e);                 
    }
Copy the code

Asynchronous functions in setTimeout cannot be captured, so we need to wrap a try/catch layer in setTimeout:

if(self.status === 'resolved') {
        promise2 = new Promise(function(resolve, reject){  
            setTimeout(function() {/ /setTimeOut Implement asynchronous try {letx = onFulfiled(self.value); //x can be a normal value, a promise, or a promise resolvePromise(promise2, x, resolve, } catch (e) {reject(e); }})})}Copy the code

step5

The final code looks like this:

function Promise (executor) {   
    let self = this;
    self.status = 'pending';
    self.value = undefined;    
    self.reason = undefined;
    self.onResolvedCallbacks = [];   
    self.onRejectedCallbacks = [];   
    function resolve(data_value) {
        if(self.status === 'pending') {
            self.status = 'resolved';
            self.value = data_value;
            self.onResolvedCallbacks.forEach(function(fn) { fn(); }}})function reject(data_reason) {
        if(self.status === 'pending') {
            self.status = 'rejected';
            self.reason = data_reason;
            self.onRejectedCallbacks.forEach(function(fn) { fn(); }) } } try { executor(resolve, reject); } catch (e) { reject(e); }}functionResolvePromise (promise2, x, resolve, reject) {// Allow others to write as much as possibleif(promise2 === x) {// There should be a type error for circular referencesreturn reject(new TypeError('Circular reference')); } // If x is a promise, promise should be an objectletcalled; // indicates whether the call succeeded or failedif(x! == null && (typeof x ==='object' ||typeof x === 'function') {// It could be a promise to see if there is one in this objectthenIf there is a try catch, let's say it's a promise. Try {let then = x.then;
            if (typeof then= = ='function') {// success then.call(x,function(y) {
                    if (called) return// Avoid the 'resolve' and 'reject' situations in which people write promises called =true;
                    resolvePromise(promise2, y, resolve, reject)
                }, function(err) {
                    if (called) return
                    called = true; reject(err); })}else{resolve(x) // ifthenIf not, return x.}} Catch (e) {if (called) return
            called = true;
            reject(e)
        }
        
    } else{// Normal valuereturn resolve(x)
    }

}

Promise.prototype.then = function(onFulfiled, onRejected) {// Success and failure are not submitted to a function onFulfiled = typeof onFulfiled ==='function'? onFulfiled:function(value) {
        return value;
    }
    onRejected = typeof onRejected === 'function'? onRejected:function(err) {
        throw err;
    }
    let self = this;
    letpromise2; // New: return promiseif(self.status === 'resolved') {
        promise2 = new Promise(function(resolve, reject){  
            setTimeout(function() {/ /setTimeOut Implement asynchronous try {letx = onFulfiled(self.value); //x can be a normal value, a promise, or a promise resolvePromise(promise2, x, resolve, } 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') {            
        promise2 = new Promise (function(resolve, reject) {   
            self.onResolvedCallbacks.push(function() {setTimeout(function(){
                    try {
                        letx = onFulfiled(self.value); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }})}); self.onRejectedCallbacks.push(function() {setTimeout(function(){
                    try {
                        letx = onRejected(self.reason); resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e); }})}); })}return promise2;
}
Copy the code

Promises – aplus-Tests were used to test this method and it passed. As shown in the figure:

test result

Of course, there are some differences between this promise and the native promise, such as no catch functionality, no static methods, and so on. These will be discussed in more detail next time.