The Promise object represents events that will happen in the future and is used to deliver messages for asynchronous operations. ECMAscript 6 natively provides Promise objects. And async/await in ES7 is also a Promise base implementation. What about Promise’s charm and power? This article will unwrap the veil and explore the principles and uses of promise. No more callback hell, no more asynchronous calls.


What is a Promise?

A promise is a promise. In JavaScript a promise refers to an object or function (an object or function that contains the then methods that are compatible with the Promise specification).
The Promise object allows asynchronous operations to be expressed as a flow of synchronous operations, avoiding layers of nested callback functions. In addition, Promise objects provide a unified interface that makes it easier to control asynchronous operations.

  1. The characteristics of the promise

1-1. Three states of Promise

  • Pending: Promise Indicates the initial state when an object instance is created
  • Resolved: This can be interpreted as a successful state
  • Rejected: Indicates the state of failure
  • If the state is pending, promise can switch to resolved or Rejected.
  • If the resolved state, then promise: cannot be switched to any other state. There must be a value and it cannot be changed.
  • If it is the Rejected state, then the promise can: Cannot be converted to any other state. There must be a reason, and the value cannot be changed.
“Value cannot be changed” means that its identity cannot be changed, not that its membership content cannot be changed.


1-3. Promise has a THEN method, which is used to specify the actions to be performed when the state of the promise object changes. The first function (onFulfilled) will be performed in resolve, and the second function (onRejected) will be performed in reject.

PromiseFn (). Then (forget (fulfilled){// If promise state becomes fulfilled, call this function},reject(onRejected){// If promise state becomes fulfilled, then(fulfilled){// If promise state becomes fulfilled, then(fulfilled){// If promise state becomes fulfilled, call this function. Call this function});Copy the code
  • Resolve and reject are optional parameters

  1. ifonFulfilledIf it is not a function, ignore it.

  2. ifonRejectedIf it is not a function, ignore it.
  • ifonFulfilledIs a function:



    1. It must be inpromiseAfter depressing, call, andpromiseValue is its first argument.
    2. It can’t be inpromiseCall before depressing.
    3. Cannot be called more than once.
  • ifonRejectedPhi is a function,



    1. It must be inpromiseRejected andpromiseReason is its first argument.
    2. It can’t be inpromiseCall before Rejected.
    3. Cannot be called more than once.
  • onFulfilledonRejectedOnly allowed inexecution contextThe stack runs only when it contains platform code.
  • onFulfilledonRejectedMust be treated as a function call (i.e. inside a function)thisundefined). 
  • For apromiseIts then method can be called multiple times.

    1. whenpromiseAfter a pity, allonFulfilledMust be executed in the order of their registration.
    2. whenpromiseA) rejected B) all C) rejected D) allOnRejectedMust be executed in the order of their registration.
  • Then must return a promise.

    promise2 = promise1.then(onFulfilled, onRejected);
    Copy the code
    1. ifonFulfilledonRejectedReturns the valuex, the Promise resolution process is executed[[Resolve]](promise2, x).
    2. ifonFulfilledonRejectedThrew an exceptione,promise2Shall be based oneforreasonBe rejected.
    3. ifonFulfilledIt’s not a function andpromise1There is a pity, thenpromise2Have to bepromise1The value of the fulfilled.
    4. ifOnRejectIt’s not a function andpromise1Rejected already, it ispromise2Must be rejected for the same reason.

 

2. Why use Promise? What are the benefits?

  • 2-1 For callback functions: can solve callback hell, as shown below:For example, if you use jQuery ajax to make multiple requests to the background for data, and each request needs to depend on each other, you need to nest callback functions to solve the problem, resulting in “callback hell.”

    $.get(url1, data1 => {
        console.log(data1,"First request"); $.get(data1. Url, data2 => {// return the url after the first request in this background console.log(data2,"Second request")... })})Copy the code
As a result, the more asynchronous logic you handle, the deeper you need to nest callbacks,Copy the code

The main problems with this encoding mode are as follows:

  1. The writing sequence of code logic is inconsistent with the execution sequence, which is not conducive to reading and maintenance.
  2. Large-scale code refactoring is required when the order of asynchronous operations changes.
  3. Callbacks are basically anonymous functions, making it difficult to track bugs.
  4. The callback function is called by third-party library code (Ajax in the example above) rather than its own business code, resulting in an IoC inversion of control.
  5. The result cannot be returned by return

What about Promise?

let p = url1 => { 
    returnnew Promise((resolve, reject) => { $.get(url, data => { resolve(data) }); })}; // p(url).then(resvloe => {return p(resvloe.url);   
}).then(resvloe2 => {
    returnresvloe2(resvloe2.url); }).then(resvloe3 => { console.log(resvloe3); }).catch(err => throw new Error(err)); When the firstthenReturns a promise, and passes the result of the returned promise to the next onethenIn the. This is the famous chain call.Copy the code
  • 2-2. Promise also has some drawbacks.
  • First, there is no way to cancel a Promise; once it is created, it is executed immediately and cannot be cancelled halfway through. Second, if you don’t set a callback function, errors thrown inside a Promise won’t be reflected externally. Third, when you are in a Pending state, you have no way of knowing what stage of progress you are currently in (just beginning or just finishing).

3, Promise use

  • 1. Create Promises. To create a Promise object, instantiate it by calling the Promise constructor with new. Excutor takes an excutor execution function as an argument, and excutor has two function type parameters, resolve reject.

let promise = new Promise(function(resolve, reject) {// Asynchronous processing // after processing, call resolve or reject});Copy the code

  • 2. State changes in promises.

1. The promise object is initialized to pending


2. Resolved => Resolved when resolve is called


3. Reject => Rejected when you call reject


  • 3. Promise object method then method

3-1. Then method registration: You can call the promise.then() method on an already instantiated Promise object, passing the resolve and reject methods as callbacks. Promise.then () is the most commonly used method for promises.


// onFulfilled is a big pity which is used to accept the successful promise // onRejected is used to accept the reason why the promise failsthenThe method is asynchronous promise. Then (onFulfilled, onRejected);Copy the code

3-2. Resolve (success): Ondepressing will be called

let promise = new Promise((resolve, reject) => {
   resolve('fulfilled'); // state by pending => depressing}); promise.then(result => { // onFulfilled console.log(result); //'fulfilled'}, reason => {// onRejected will not be called})Copy the code

3-3. Reject: onRejected is called

let promise = new Promise((resolve, reject) => {
   reject('rejected'); // State by pending => Rejected}); Then (result => {// onFulfilled will not be called}, reason => {// onRejected console.log(reason); //'rejected'
})
Copy the code

3-4. Promise. catch method: Catch errors

(The catch method is an alias for Promise.then (null, Rejection), which specifies the callback when an error occurs.)Copy the code

Promise.catch (onRejected) = promise.then(null, onRejected); // This is a big pity. // onRejected can't catch the promise. Then (onRejected, onrfulfilled); // Can be written: promise. Then (onpity). Catch (onRrejected);Copy the code

3-5. The promise chain method returns a new promise object each time it is called


A static method for Promise

function step1() {
    console.log("step1");
}
function step2() {
    console.log("step2");
}
function onRejected(error) {
    console.log("Wrong way:", error); } var promise = Promise.resolve(); Promise.then (step1). Then (step2). Catch (onRejected) // catch(onRejected)thenExceptions in methodsCopy the code

4. Promise’s method is to use

  1. Promise. Resolve returns a fulfilled Promise object
Promise.resolve('success'); / / equivalent tolet promise = new Promise(resolve => {
   resolve('success');
});
Copy the code

// The library implements promise.resolve =function (val) {  return new Promise((resolve, reject) => resolve(val))}
Copy the code

Reject returns a Promise object in the Rejected state

var p = Promise.reject('Wrong');
 
p.then(null, function(s){ console.log(s) }); / / make a mistakeCopy the code

3.Promise.all accepts an array of Promise objects as arguments

Only all resolve is called, which is typically used to handle multiple parallel asynchronous operations

const p1 = new Promise((resolve, reject) => { resolve(1); }); const p2 = new Promise((resolve, reject) => { resolve(2); }); const p3 = new Promise((resolve, reject) => { reject(3); }); Promise.all([p1, p2, p3]).then(data => { console.log(data); // [1, 2, 3] Results are in the same order as the array of Promise instances}, err => {console.log(err); });Copy the code

// The library implements promise.all =function(arr) {    
 return new Promise((resolve, reject) => {  
      let num = 0,innerArr = [];
       function done(index,data){   
         innerArr[index] = data;    
        num ++;           
 if(num === arr.length){ resolve(innerArr); }}for(leti =0 ; i<arr.length; i++){ arr[i].then((res)=>{done(i,res); },reject); // If there is a failure, return}})}Copy the code


4.Promise.race accepts an array of Promise objects as arguments

As long as one of the Promise objects enters the FulFilled or Rejected state, the following processing will be continued.

function timerPromisefy(delay) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
var startDate = Date.now();

Promise.race([
    timerPromisefy(10),
    timerPromisefy(20),
    timerPromisefy(30)
]).then(function (values) {
    console.log(values); // 10
});
Copy the code

// The library implements promise.race =function(arr) {  
  returnnew Promise((resolve, reject) => { arr.forEach((item, index) => { item.then(resolve, reject); }); }); }Copy the code

5. Promise finally

Promise.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};Copy the code


5. Prmoise code base implementation


/ Promise to realize the following Promise/A * * * * Promise/A + + specification standard translation: * * / / https://promisesaplus.com/ / judge whether x Promise according to specificationfunctionResolvePromise (promise2, x, resolve, reject) {// If x and promise2 point to the same value, use TypeError to reject the promise. (Will cause circular reference error)if (promise2 === x) { 
       return reject(new TypeError('Circular reference')); } // Avoid multiple callslet isUsed = false; // if x is a promise object (thenable is a promise objectthen/** * If x is pending, the promise must keep pending until X resolved or Rejected. If x is resolved, use the value of x for resolve Promise. If x is rejected, apply the x reason to reject promise.. * / / /if(x instanceof Promise) {// Get its end value to continue resolve //if (x.status === 'pending'// resolvePromise(promise2, y, resolve, reject); // resolvePromise(promise2, y, resolve, reject); // }, reason => { // reject(reason); / /}); / /}else{// If x is already in the execute/reject state (the value has been resolved to a normal value), execute with the same value pass promise // x.tran (resolve, reject); // ** * // ** 1 willthen// 2. If an exception is thrown when taking the value x. teng, the promise is rejected as the reason for the exception. / / 3. IfthenIs a function called x for thisthenResolve, reject, reject, and: // 3-1. When resolve is called with y, execute [[resolve]](promise, y). // 3-2. When reject is called with reason, the promise is rejected with reason. If both resolvee and reject are called, or if they are called multiple times, then only the first call is valid and the rest is ignored. // 3-4then// If resolve or reject has already been called, it is ignored. isUsed =true; // Otherwise, use eas a reason to reject the promise. / / 4. IfthenResolve promise is not a function, then x is the value. / / / / / *}else    
 if(x ! == null && ((typeof x ==='object') || (typeof x === 'function'))) {try {// is the thenable object (yesthenIf an exception is thrown when taking the value x.teng, reject the promise as the reason for that exception.let then = x.then;
            if (typeof then= = ='function') {thene.call (x, y => {// If y is a promise, continue to parse the promise recursivelyif (isUsed) return; 
                   isUsed = true; resolvePromise(promise2, y, resolve, reject); }, reason => {// If you fail, you failif (isUsed) return; 
                   isUsed = true; reject(reason); })}elseResolve promise {// resolve promise is a function. resolve(x); } } catch (e) {if (isUsed) return; 
           isUsed = true; reject(e); }}else{// resolve promise if x is not an object or a function. For example, x = 123 or x ='success'resolve(x); }} // The Promise object represents future events that will be used to deliver messages for asynchronous operations. // The Promise object, which allows asynchronous operations to be expressed as synchronous operations, avoiding layers of nested callback functions. In addition, Promise objects provide a unified interface that makes it easier to control asynchronous operations. Class Promise {// Promise is a class, Resolve reject /** * var Promise = new Promise(resolve reject /** * var Promise = new Promise)function(resolve, reject) {// immediately execute // asynchronous processing // after processing, call resolve or reject // Resolve (...) when asynchronous code executes successfully. Reject (...) is called when asynchronous code fails. // In this case, we usesetTimeout(...) To simulate asynchronous code, which could be an XHR request or some HTML5 API method.setTimeout(function(){  
    resolve("Success!"); // The code works fine! }, 250); }); promise.then(function(successMessage){//successMessage values resolve(...) // The successMessage argument doesn't have to be a string, but as an example console.log("Yay! " + successMessage); 
       });    
      */    

constructor(ex) {  // 

  this.status = 'pending'; // This. ResolveVal = undefined; This. RejectVal = undefined; / / rejected state (failure) the information returned enclosing onResolveCallBackFns = []; // Store the onResolved function corresponding to the Resolved state (because chain calls can be multiplethenMethod) this.onRejectCallBackFns = []; /** * A Promise must be in one of the states: Pending, depressing or Rejected. ** If the state is pending, then the Promise will be fulfilled: You can switch to resolved or Rejected. If the resolved state, then promise: cannot be switched to any other state. There must be a value and it cannot be changed. If it is the Rejected state, then the promise can: Cannot be converted to any other state. There must be a reason and the value cannot be changed." "Value cannot be changed" means that its identity cannot be changed, not that its member contents cannot be changed. * /letResolve = (data) => {// data Specifies the final value to be received when the state is successfulif (this.status === 'pending'Resolve reject) this.status ='resolved'; this.resolveVal = data; this.onResolveCallBackFns.forEach(cb => cb()); }}let reject = (err) => { 
           if (this.status === 'pending') {// Only the pedning state => Reject state resolve reject) this.status ='rejected'; this.rejectVal = err; this.onRejectCallBackFns.forEach(cb => cb()); // new Promise((resolve, reject) => {// throw new Error()'error in ex') // }) try { ex(resolve, reject); } catch (e) { reject(e); }} // According to prmoise A + specificationthenThe method takes two argumentsthenMethods that are executed asynchronously must return a promisethen(resolve, reject) {        //  thenThe resolve,reject methods are optional arguments that ensure execution continues. If resolve is not a function, ignore it. If reject is not a function, it is ignored. resolve = typeof resolve =='function' ? resolve : y => y;     
       reject = typeof reject == 'function' ? reject : err => { throw err };

        let promise2; // thenYou must return a promiseif (this.status === 'pending'// resolve/reject = new Promise((res, rej) => { this.onResolveCallBackFns.push(() => {set// Resolve () => {try {// resolvePromise resolves x and promise2 /** If resolve or reject returns x, The Promise resolution process [[Resolve]](promise2, x) is executed. // If Resolve or Reject throws exception E, the promise2 should be rejected with e as rejectVal. // If resolve is not a function and promise1 is resolved // If reject is not a function and promise1 has rejected, promise2 must be rejected with the same rejectVal. */letx = resolve(this.resolveVal); resolvePromise(promise2, x, res, rej); } catch (e) { rej(e); }}, 0); }); this.onRejectCallBackFns.push(() => {setTimeout(() => { 
                       try { 
                           letx = reject(this.rejectVal); resolvePromise(promise2, x, res, rej); } catch (e) { rej(e); }}, 0); }); }); }if (this.status == 'resolved') {// It must be called after promise Resolved with the promise value as its first argument. Promise2 = new Promise((res, rej) => {// useset// The method is asynchronous // 2. For a promise, itsthenMethod can be called multiple times (when the same promise is called multiple times in other programs)then(Resolved/Rejected), and ensure that resolve/reject can be executed asynchronously after resolved/ RejectedsetTimeout(() => { 
                   try { 
                      letx = resolve(this.resolveVal); resolvePromise(promise2, x, res, rej); // // resolvePromise resolves the relationship between X and promise2} catch (e) {rej(e); }})})}if (this.status == 'rejected'Promise2 = new Promise((res, rej) => {// This method is asynchronous, so it is usefulsetThe Timeout methodsetTimeout(() => {
                    try { 
                       letx = reject(this.rejectVal); resolvePromise(promise2, x, res, rej); // resolvePromise resolves the relationship between x and promise2} catch (e) {rej(e); }}); })}returnpromise2; / / callthenReturn a new promise} // catch only receives an error catchthenShort for unsuccessful catch(err) {returnthis.then(null, err); }} /** * Promise. All Promise is parallel processing * parameter: arR array as a parameter * return value: Return a Promise instance * Resolve is resolved when all the Promise objects in this array are in the resolve state. */ Promise.all =function (arr) {
    return new Promise((resolve, reject) => {
        let num = 0, innerArr = [];
        function done(index, data) {
            innerArr[index] = data;
            num++; 
           if(num === arr.length) { resolve(innerArr); }}for (let i = 0; i < arr.length; i++) { 
           arr[i].then((res) => {
                done(i, res); }, reject); Race: receives an array of Promise objects as arguments. Race: receives an array of Promise objects as arguments. Return a Promise instance * As soon as one of the Promise objects enters the Resolved or Rejected state, the process will proceed (depending on which is faster) */ promise.race =function (arr) {
    returnnew Promise((resolve, reject) => { arr.forEach((item, index) => { item.then(resolve, reject); }); }); } // promise. reject returns a Promise object in rejected state promise.resolve =function (val) {  
  returnNew Promise((resolve, reject) => resolve(val))} //.Promisefunction (val) {   
 returnnew Promise((resolve, reject) => reject(val)); } Promise.deferred = Promise.defer =function () {
    let dfd = {};
    dfd.promise = new Promise((resolve, reject) => {
        dfd.resolve = resolve;
        dfd.reject = reject; 
   }) 
   returndfd; } module.exports = Promise;Copy the code


6, test,

let p = new Promise((resolve, reject) => { 
 reject('err'); }) p.then().then().catch(r => { console.log(r); }).then(data => {  
console.log('data', data); }) errdata undefinedCopy the code

let fs = require('fs');
function read() {// the advantage is to solve the nesting problem // the disadvantage is not convenient error handlinglet defer = Promise.defer(); 
 fs.readFile('./1.txt'.'utf8', (err, data) => { 
   if (err) defer.reject(err); 
   defer.resolve(data)  }); 
 returndefer.promise; }read().then(data => { console.log(data); }); Execute result I am 1.txt contentCopy the code