Introduction to Promise's correct posture

Posted by Xyox on Sun, 19 May 2019 03:56:15 +0200

Promise is a solution to asynchronous programming. Syntactically, Promise is an object from which messages for asynchronous operations can be obtained.

Basic usage of Promise

The Promise constructor accepts a function as a parameter, and the two parameters of the function are resolve and reject, respectively. They are two functions, provided by the JavaScript engine.

  • The purpose of the resolve function is to change the state of the Promise object from "incomplete" to "successful" (that is, from Pending to Resolved), call it when the asynchronous operation succeeds, and pass the result of the asynchronous operation as a parameter.
  • The reject function changes the state of the Promise object from "incomplete" to "failed" (that is, from Pending to Rejected), calls when an asynchronous operation fails, and passes the error reported by the asynchronous operation as a parameter.
  • The then method can accept two callback functions as parameters. The first callback function is called when the state of the Promise object changes to Resolved, and the second callback function is called when the state of the Promise object changes to Reject.
var promise = new Promise(
    //Asynchronous execution,Promise Objects are created and executed immediately
    function (resolve,reject) {
        //Long-time asynchronous operation
  if('Successful asynchronous processing') {  
        resolve();    //Call when data processing is successful
  } else {
        reject();    //Called when data processing fails
    }
        
    }
)
//Promise After the instance is generated, it can be used then Method is specified separately Resolved State and Reject The callback function of the state.
promise.then(
    function A() {
        //Data Processing Successfully Executed
    },
    function B() {
        //Execution after data processing failure
    }
)

 

Let's give a simple example to simulate the running process of asynchronous operation success and asynchronous operation failure functions.

 

console.log('starting');
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2 Seconds later, I ran it.");
        resolve('Asynchronous operation succeeded');     //1
        //reject('Asynchronous operation failed');    //2
     //return 'hello';       //3
}, 2000) }).then(function (value) { console.log('Execute me after the asynchronous operation succeeds:',value); }, function (value) { console.log('Execute me after an asynchronous operation fails:',value); } ) console.log('I'm running, too.'); // In the above code, one code call, the order of output is: //starting //I'm running, too.
//Two seconds later, I ran it. // Execute me after the asynchronous operation succeeds: Asynchronous operation succeeded // In the above code, there are two code calls in the order of output: //starting //I'm running, too.
//Two seconds later, I ran it. // Execute me after an asynchronous operation fails: Asynchronous operation failed


//There are three code calls in the above code. The order of output is:
//starting
//I'm running, too.
//Two seconds later, I ran it.

Knowing that the return'hello'statement at code 3 is not returned as a parameter to the then() function in the newly created new Promise object, will it be returned to promise? Let's test it with a piece of code.

console.log('starting');
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2 Seconds later, I ran it.");
        resolve('Asynchronous operation succeeded');     //1
        //reject('Asynchronous operation failed');    //2
        return 'hello';
    }, 2000) 
    
})
promise.then(function (value) { 
    console.log('Execute me after the asynchronous operation succeeds:',value);
},
function (value) {
    console.log('Execute me after an asynchronous operation fails:',value);
}
)
console.log('I'm running, too.');
console.log(promise);
setTimeout(function () {
    console.log('5 Seconds later, I executed it.');
    console.log(promise);
},5000);


//starting
//I'm running, too.
//Promise { pending }
  //[[PromiseStatus]]:"pending"
  //[[PromiseValue]]:undefined
  //__proto__:Promise {constructor: , then: , catch: , ...}
//2 Seconds later, I ran it.
//Execute me after the asynchronous operation succeeds: Asynchronous operation succeeded
//5 Seconds later, I executed it.
//Promise { resolved }
  //[[PromiseStatus]]:"resolved"
  //[[PromiseValue]]:"Asynchronous operation succeeded"
  //__proto__:Promise {constructor: , then: , catch: , ...}

From the execution results, the variable promise is still an instance of the new Promise object. So although the return statement is executed, it has no effect on the promise instance, which is equivalent to nonexistence.

From the code tested above, Promise objects have the following two characteristics.
(1) The state of the object is not affected by the outside world. Promise objects represent an asynchronous operation in three states: Pending (in progress), Resolved (completed, also known as Fulfilled) and Rejected (failed). Only the result of an asynchronous operation can determine which state is currently in.

(2) Once the state changes, it will not change again, and this result can be obtained at any time. There are only two possibilities for the state change of Promise objects: from Pending to Resolved and from Pending to Rejected. As long as these two situations occur, the state solidifies, will not change any more, will keep this result. Even if the change has happened, you can immediately get the result by adding a callback function to the Promise object. This is completely different from Event, which is characterized by the fact that if you miss it and listen on it, you will not get results.

resolve(value) VS resolve(Promise)

When the asynchronous operation is successful, we call the resolve function, which changes the state of the Promise object from Pending to Resolved, and passes the result of the asynchronous operation as a parameter to the first function in the then() method.

So what's the difference between an incoming parameter being a value and an incoming parameter being a promise object? Let's look at an example.

When the incoming parameter is a value:

var time = new Date();
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("1 Seconds later, I ran it.");
        resolve('Asynchronous operation succeeded');     //1
    }, 2000) 
    
}).then(function (value) {
    console.log(value,new Date() - time);
})
//The output of the execution is:
//2 Seconds later, I ran it.
//Asynchronous operation succeeded in 1002

After about a second, we can see that in the callback method in the resolved state, we print out the contents of the comments above. We can pass the results of operations through the resolve method and then use them in the callback method.

What if we pass in a Promise instance in resolve?

var time = new Date();
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2 Seconds later, I ran it.");
        resolve('Asynchronous operation succeeded');     //1
    }, 2000) 
    
})

var promise2 = new Promise(function (resolve,reject) {
    setTimeout(resolve,1000,promise);
}).then(function (value) {
    console.log(value,new Date() - time);
})

//The output after execution is:
//
2 Seconds later, I ran it. //Asynchronous operation succeeded in 2003

promise2 took two seconds to print out the results. Strangely, don't we set promise2 to execute after one second?

Simply put, because the resolve() function in promise2 passes into the promise object, the state of the promise object determines the state of the promise, and the return value is passed to the promise.

Promise/A+stipulates [[Resolve] (promise, x)

2.3.2. If x is a promise instance, the state of X is the state of promise.

2.3.2.1. If the state of x is pending, then the state of promise will also be pending until the state of x changes.

2.3.2.2. If the state of x is fulfilled, the state of promise is fulfilled, and the invariant value of x is used as the invariant value of promise.

2.3.2.3. If the state of x is rejected, the state of promise is rejected, and the invariant cause of x is the invariant cause of promise.

2.3.4. If x is not an object or function, the promise state is converted to fulfilled and X is used as the invariant value of promise.

 Promise.prototype.then()

Promise instances have the then method, which is defined on the prototype object Promise.prototype. Its purpose is to add a callback function for the Promise instance when the state changes. As mentioned earlier, the first parameter of the then method is the callback function of the Resolved state, and the second (optional) parameter is the callback function of the Rejected state.

The then method returns a new Promise instance (note, not the original Promise instance). So we can use chain writing, that is to say, call another then method after the then method.

var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2 Seconds later, I ran it.");
        resolve('Asynchronous operation succeeded');     //1
    }, 2000) 
    
})
promise.name = 'promise';
console.log('promise:',promise)
var promise2 = promise.then(function (value) {
    console.log(value);
})
promise2.name = 'promise2';
console.log('promise2:',promise2);

// promise:
//     Promise { pending }
//     [[PromiseStatus]]:"pending"
//     [[PromiseValue]]:undefined
//     name:"promise"
//     __proto__:Promise {constructor: , then: , catch: , ...}
// promise2:
// Promise { pending }
//     [[PromiseStatus]]:"pending"
//     [[PromiseValue]]:undefined
//     name:"promise2"
//     __proto__:Promise {constructor: , then: , catch: , ...}

 

We can see that the promise.then() method returns a new Promise object after execution. That is to say, promise2 in the code above is a Promise object. Its implementation effect is the same as that in the code below, but in the then() method, the JS engine has done it for us automatically.

 

promise2 = new Promise(function (resolve,reject) {})

 

Now that I have automatically implemented a promise object in the then() function, how can I pass parameters to the resolve() or reject() functions? In fact, in the then() function, we can return() to resolve() or reject() of promise2. Look at an example.

//var time = new Date();
var promise = new Promise(function(resolve, reject) {  
    setTimeout(function() { 
        console.log("2 Seconds later, I ran it.");
        resolve('Asynchronous operation succeeded');     //1
        //reject('Asynchronous operation failed');    //2
    }, 2000) 
    
})
//console.log('promise:',promise)
var promise2 = promise.then(function (value) {
    console.log(value);
    //resolve('nihao');  //Report errors. Note that you cannot write the resolve() method here, because there is no solution method in the then function.
    return (1);       
    //return promise;   //You can also return a promise object, which returns the parameter values inside resolve('parameter value') or reject('parameter value') after the promise object is executed.
  //If you don't write return, the default return is undefined.
}) var promise3 = promise2.then(function (value) { console.log('is:',value); },function (value) { console.log('error:',value); }) promise2.name = 'promise2'; setTimeout(function () { console.log(promise2); },3000); //2 Seconds later, I ran it. //Asynchronous operation succeeded //is: 1 //Promise {resolved} //name:"promise2" //__proto__:Promise //[[PromiseStatus]]:"resolved" //[[PromiseValue]]:1

Promise and Error State Processing

then(null, rejection), which specifies the callback function to be performed when an asynchronous operation fails. Let's give an example.

var promise = new Promise(function(resolve, reject) {
    setTimeout(function () {
            reject('error');
    },2000);
}).then(null,function(error) {
    console.log('rejected', error)
});
//rejected error

We know that the then() method returns a promise object after execution, so we can also call the then() method, but in order to capture exception information, we need to bind A. then(null, rejection) for each then() method. Because the error information of the Promise object is "bubbling" in nature, the error is passed back until it is caught. So Promise provides us with a prototype function Promise.prototype.catch() to make it easier to catch exceptions.

Let's look at an example.

 

var promise = new Promise(function(resolve, reject) {
    setTimeout(function () {
            reject('error');
    },2000);
}).then(function(value) {
    console.log('resolve', value);
}).catch(function (error) {
    console.log(error);
})
//Operation result
//error

In the code above, there are two Promise objects: one generated by promise and one generated by the then. Any error thrown by them will be caught by the last catch.

But if we use the. then(null, rejection) method to process error information, we need to return the state of the last exception information in each rejection() method, which will affect the clarity and logic of the code when more than one then() method is called.

So, in general, instead of defining the callback function of the Reject state in the then method (that is, the second parameter of the then), always use the catch method.

Topics: Javascript Programming