Promise

  • PromiseIs a solution to asynchronous programming that is more reasonable and powerful than the traditional asynchronous solutions of callback functions and events. It is now incorporated into the specification by ES6;
  • PromiseFixed the pain point of “callback hell”

Basic API

attribute

  • Promise.length
    • lengthProperty, whose value is always 1 (the number of constructor arguments).
  • Promise.prototype
    • saidPromiseThe prototype of the constructor.

Class method

  • Promise.resolve(value)
    • Returns a state given by a givenvalueDecision of thePromiseObject.
    • If the value isthenable(that is, withthenMethod object), returnedPromiseThe final state of the object is determined by the execution of the then method;
    • Otherwise (thevalueNull, basic type or nothenMethod object), returnedPromiseThe object state isfulfilled(resolved), and will thevaluePass it to the correspondingthenMethods;
    • In general, if you don’t know whether a value isPromiseObject, usingPromise.resolve(value)To return aPromiseObject, so that thevalueIn order toPromiseObject form use;
  • Promise.reject(reason)
    • Returns a failedPromiseObject and passes the given failure information to the corresponding handler
  • Promise.race(iterable)
    • when可迭代Any child of the argumentpromiseTo be executed first after a success or failurepromiseFather),promiseI can use them soonpromiseThe success return value or failure details of the call parent as argumentspromiseBinds to the corresponding handle and returns thatpromiseobject
  • Promise.allSettled(iterable)
    • Return a promise resolved after all the given promises have been resolved or rejected, with an array of objects, each representing thepromiseThe results of
  • Promise.all(iterable)
    • This method returns a new onepromiseObject,
    • thepromiseObjects in the可迭代Parameter objectpromiseSuccess is triggered when all objects succeed
    • Once there is any one可迭代The inside of thepromiseThis is triggered immediately if the object failspromiseObject failure
    • This newpromiseAfter the successful state is triggered, the object contains a可迭代In allpromiseAn array of return values as the return values of the successful callback, in order可迭代In the same order;
    • If this newpromiseThe object triggers the failed state, and it will put可迭代The first trigger to failpromiseObject as its failure error message.
    • Promise.allMethods are often used to process more than onepromiseObject’s collection of states.

The prototype

The prototype property

  • Promise.prototype.constructor
    • Returns the created instance function. The default isPromisefunction
    • The constructor is executed only once

Prototype method

  • Promise.prototype.then(onFulfilled, onRejected)
    • Add solution (fulfillment) and rejection (rejection) callback to currentpromise, returns a new onepromise, will return the value of the callbackresolve;
    • OnRejected catches the exception thrown by the last THEN and does not catch the current oneonFulfilledAn exception thrown by
  • Promise.prototype.catch(onRejected)
    • Add a reject (rejection) callback to currentpromise, returns a new onepromise.
    • When this callback function is called, the newpromiseResolve will be resolved with its return value, otherwise if the currentpromiseEnter thefulfilledStatus, the currentpromiseThe completed results as newpromiseThe completion result of.
  • Promise.prototype.finally(onFinally)
    • Adds an event handling callback to the currentpromiseObject, and in the originalpromiseAfter the object is parsed, a new one is returnedpromiseObject.
    • The callback will be in the presentpromiseCall after run, regardless of currentpromiseThe state is complete (fulfilled) or fail (rejected)

The core points

• process.nexttick and promise.then are microtasks, while setImmediate, setTimeout, and setInterval are macrotasks, where event loops perform the macrotask first. All microtasks are then executed • The Promise constructor is executed synchronously, and the functions in promise.then are executed asynchronously

const promise = new Promise((resolve, reject) = > {
  // The constructor executes synchronously
  console.log(1);
  resolve();
  console.log(2);
});
promise.then((a)= > {
  // Execute asynchronously
  console.log(3);
});
console.log(4);

// ------------ Result -------------
1;
2;
4;
3;
Copy the code
  • promiseThere are three states:pending,Resolved (fulfilled)rejected. State change can only bepending->resolvedorpending->rejectedOnce a state is changed it cannot be changed.
    • pending: Initial state, neither successful nor failed state.
    • resolved(fulfilled): indicates that the operation completed successfully.
    • rejected: Indicates that the operation fails.
const p1 = new Promise((resolve, reject) = > {
  setTimeout((a)= > {
    resolve('success')},1000)});const p2 = promise1.then((a)= > {
  throw new Error('error!!! ')});console.log('promise1', p1);
console.log('promise2', p2);
setTimeout((a)= > {
  console.log('promise1', p1)
  console.log('promise2', p2)
}, 2000);

// ------------ Result -------------
promise1 Promise {<pending>}
promise2 Promise {<pending>}
Uncaught (in promise) Error: error!!!
    at <anonymous>:7:9
Promise.then (async)
promise1 Promise {<resolved>: "success"}
promise2 Promise {<rejected>: Error: error!!!
    at <anonymous>:7:9}
Copy the code
  • ConstructorresolverejectOnly the first execution is valid; multiple calls do nothing
new Promise((resolve, reject) = > {
  resolve("resolve1");
  reject("reject");
  resolve("resolve2");
})
  .then((res) = > {
    console.log("then: ", res);
  })
  .catch((err) = > {
    console.log("catch: ", err);
  });

// ------------ Result -------------
then: resolve1;
Copy the code
  • promiseEach call.thenor.catchWill return a new onepromise, thus realizing the chain call
const p1 = Promise.resolve(1); / / return Promise
/ / return Promise
const p2 = p1
  .then((res) = > {
    console.log(res);
    return 2;
  })
  .catch((err) = > {
    return 3;
  });
/ / return Promise
const p3 = p2.then((res) = > {
  console.log(res);
});

// ------------ Result -------------
1;
2;
Copy the code
  • promise.thenor.catchIt can be called multiple times, but herePromiseThe constructor is executed only once.
    • promiseOnce the internal state changes and there is a value, then each subsequent call.thenor.catchI’m going to go straight to that value
const p5 = new Promise((resolve, reject) = > {
  // The constructor is executed only once
  setTimeout((a)= > {
    console.log('start')
    resolve('success'); // This is the first time
        resolve('success1'); // The execution is invalid
  }, 1000)
})
p5.then((res) = > {
  console.log('promise1', res);
});
p5.then((res) = > {
  console.log('promise2', res)
});

// ------------ Result -------------
start
promise1 success
promise2 success
Copy the code
  • .thenYou can take two arguments, the first for the function that handled the success and the second for the function that handled the error.
    • .catch.thenA convenient way to write the second parameter;
    • .thenThe second error-handling function cannot catch the error thrown by the first successful function, while the subsequent.catchPrevious errors can be caught
Promise.resolve().then(function success1 (res) {
    throw new Error('error'); // The first THEN throws an error before the next THEN receives it
  }, function fail1 (e) {
    console.error('error1: ', e)
}).then(function success2 (res) {}, function fail2 (e) {
    // Receive the error raised by the previous THEN
    console.error('error2: ', e)
});

// ------------ Result -------------
error2:  Error: error
    at success1 (<anonymous>: 2:11)Copy the code
  • .thenor.catchThe argument to is expected to be a function. Passing in a non-function will result in value penetration
Promise.resolve(1)
  .then(2) // This step will penetrate
  .then(Promise.resolve(3)) // promise.resolve (3) is the Promise object
  .then(console.log); // The first resolve 1 is received

// ------------ Result -------------
1;
Copy the code

To build the wheels

structure

function Promise(fn) {}

// interrupt to throw the result set
Promise.resolve = function (value) {};
// interrupt to throw an exception
Promise.reject = function (reason) {};
// Returns the fastest result of multiple Promise collection requests
Promise.race = function (可迭代) {};
// Returns all normal or abnormal result sets from multiple Promise sets
Promise.allSettled = function (可迭代) {};
// Return all normal result sets from multiple Promise sets
Promise.all = function (可迭代) {};

// The prototype method then returns a new promise to form a chain call
Promise.prototype.then = function (onResolved, onRejected) {};
// The prototype method catch throws an exception
Promise.prototype.catch = function (onError) {};
// The prototype method promises the last processing after a normal or an exception
Promise.prototype.finally = function (onFinally) {};
Copy the code

Promise constructor

function resolve(value) {
  var _this = this;

  // If the state is not pending, it is not executed
  if(_this._status ! = ="pending") return;

  setTimeout(function () {
    _this._status = "fulfilled";
    _this._value = value;
    _this._onResolvedFns.forEach(function (cb) {
      cb && cb();
    });
  });
}

function reject(error) {
  var _this = this;

  // If the state is not pending, it is not executed
  if(_this._status ! = ="pending") return;

  setTimeout(function () {
    _this._status = "rejected";
    _this._error = error;
    _this._onRejectedFns.forEach(function (cb) {
      cb && cb();
    });
  });
}

function isFunction(func) {
  return typeof func === "function";
}

function isObject(func) {
  return typeof func === "object";
}

function Promise(fn) {
  if(! isObject(this)) {
    throw new TypeError("Promise must be the object instantiated by new.");
  }
  if(! isFunction(fn)) {throw new TypeError("The Promise constructor entry must be a function");
  }

  / / state pending/fulfilled/rejected
  this._status = "pending"; / / the default pending
  this._value = null; / / value
  this._error = null; / / exception

  // Successful callback
  this._onResolvedFns = [];
  // Failed callback
  this._onRejectedFns = [];

  try {
    // Bind the current context
    fn(resolve.bind(this), reject.bind(this));
  } catch(e) { reject(e); }}Copy the code

Promise.prototype.thenChain callback

/** * parse promise * * * @param {*} promise * @param {*} result Callback * @param {*} resolve * @param {*} reject */
function resolvePromise(promise, result, resolve, reject) {
  // Cyclic reference detection
  if (promise === result) return reject(new TypeError("Circular reference"));

  if (result instanceof Promise) {
    result.then(function (newResult) {
      resolvePromise(promise, newResult, resolve, reject);
    }, reject);
  } else if (isObject(result) || isFunction(result)) {
    if (result === null) return resolve(result);

    var then;

    try {
      then = result.then;
    } catch (error) {
      return reject(error);
    }

    if(! isFunction(then))return resolve(result);

    var called = false; / / call the lock

    try {
      var _thenLock = function (cb) {
        // prevent another call
        if (called) return;
        called = true; / / tag lock
        cb && cb();
      };

      // then stream calls
      then.call(
        result,
        function (nextResult) {
          _thenLock(function () {
            resolvePromise(promise, nextResult, resolve, reject);
          });
        },
        function (r) {
          // If you fail, you fail
          _thenLock(function () { reject(r); }); }); }catch (e) {
      _thenLock(function () { reject(e); }); }}else{ resolve(result); }}// The prototype method then returns a new promise to form a chain call
Promise.prototype.then = function (onResolved, onRejected) {
  // Then receives two functions. If they are not functions, the value of the previous THEN continues downward
  onResolved = isFunction(onResolved)
    ? onResolved
    : function (y) {
        return y;
      };
  onRejected = isFunction(onRejected)
    ? onRejected
    : function (err) {
        throw err;
      };

  var _this = this;

  var promise = new Promise(function (resolve, reject) {
    if (_this._status === "pending") {
      / / pending state
      // Save callback successfully
      _this._onResolvedFns.push(function () {
        setTimeout(function () {
          try {
            resolvePromise(promise, onResolved(_this._value), resolve, reject);
          } catch(error) { reject(error); }}); });// Store the failed callback
      _this._onRejectedFns.push(function () {
        setTimeout(function () {
          try {
            resolvePromise(promise, onRejected(_this._error), resolve, reject);
          } catch(error) { reject(error); }}); }); }else {
      setTimeout(function () {
        try {
          // this is a big pity/Rejected
          resolvePromise(
            promise,
            _this._status === "fulfilled"
              ? onResolved(_this._value)
              : onRejected(_this._error),
            resolve,
            reject
          );
        } catch(error) { reject(error); }}); }});return promise;
};
Copy the code

Promise.prototype.catchException handling

// The prototype method catch throws an exception
Promise.prototype.catch = function (onError) {
  // Catch method is short for then method failed
  return this.then(null, onError);
};
Copy the code

Promise.prototype.finallyThe end of the call

// The prototype method promises the last processing after a normal or an exception
Promise.prototype.finally = function (onFinally) {
  var _finally = Promise.resolve(onFinally());

  return this.then(
    function (value) {
      return _finally.then(function () {
        return value;
      });
    },
    function (error) {
      return _finally.then(function () {
        throwerror; }); }); };Copy the code

Promise.resolve

// interrupt to throw the result set
Promise.resolve = function (value) {
  return new Promise(function (resolve) {
    resolve(value);
  });
};
Copy the code

Promise.reject

// interrupt to throw an exception
Promise.reject = function (reason) {
  return new Promise(function (resolve, reject) {
    reject(reason);
  });
};
Copy the code

Promise.all

// Return all normal result sets from multiple Promise sets
Promise.all = function (可迭代) {
  return new Promise(function (resolve, reject) {
    var array = Array.prototype.slice.call(iterable);
    if(! array.length)return resolve([]);

    var results = [];
    var count = 0;

    for (var i = 0; i < array.length; i++) {
      (function (i) {
        Promise.resolve(array[i])
          .then(function (res) {
            results[i] = res;
            count++;

            if (count === array.length) {
              resolve(results);
            }
          })
          .catch(function (error) { reject(error); }); })(i); }}); };Copy the code

Promise.race

// Returns the fastest result of multiple Promise collection requests
Promise.race = function (可迭代) {
  return new Promise(function (resolve, reject) {
    / / shallow copy
    var array = Array.prototype.slice.call(iterable);
    for (var i = 0; i < array.length; i++) {
      Promise.resolve(array[i]).then(resolve, reject); }}); };Copy the code

Promise.allSettled

// Returns all normal or abnormal result sets from multiple Promise sets
Promise.allSettled = function (可迭代) {
  return new Promise(function (resolve, reject) {
    var array = Array.prototype.slice.call(iterable);
    if(! array.length)return resolve([]);

    var results = [];
    var count = 0;

    for (var i = 0; i < array.length; i++) {
      (function (i) {
        Promise.resolve(array[i]).finally(function (value) {
          results[i] = res;
          count++;

          if(count === array.length) { resolve(results); }}); })(i); }}); };Copy the code

Promise/A+test

  • The installationpromises-testsTest package
  • Writing test filespromise.spec.js
var Promise = require("./promise");

Promise.deferred = function () {
  var result = {};
  result.promise = new Promise(function (resolve, reject) {
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
};

module.exports = Promise;
Copy the code
  • Executing the test scriptpromises-tests promise.spec.js
  • Check that the use case is successfully executed

The complete code

function resolve(value) {
  var _this = this;

  // If the state is not pending, it is not executed
  if(_this._status ! = ="pending") return;

  setTimeout(function () {
    _this._status = "fulfilled";
    _this._value = value;
    _this._onResolvedFns.forEach(function (cb) {
      cb && cb();
    });
  });
}

function reject(error) {
  var _this = this;

  // If the state is not pending, it is not executed
  if(_this._status ! = ="pending") return;

  setTimeout(function () {
    _this._status = "rejected";
    _this._error = error;
    _this._onRejectedFns.forEach(function (cb) {
      cb && cb();
    });
  });
}

function isFunction(func) {
  return typeof func === "function";
}

function isObject(func) {
  return typeof func === "object";
}

/** * parse promise * * * @param {*} promise * @param {*} result Callback * @param {*} resolve * @param {*} reject */
function resolvePromise(promise, result, resolve, reject) {
  // Cyclic reference detection
  if (promise === result) return reject(new TypeError("Circular reference"));

  if (result instanceof Promise) {
    result.then(function (newResult) {
      resolvePromise(promise, newResult, resolve, reject);
    }, reject);
  } else if (isObject(result) || isFunction(result)) {
    if (result === null) return resolve(result);

    var then;

    try {
      then = result.then;
    } catch (error) {
      return reject(error);
    }

    if(! isFunction(then))return resolve(result);

    var called = false; / / call the lock

    try {
      var _thenLock = function (cb) {
        // prevent another call
        if (called) return;
        called = true; / / tag lock
        cb && cb();
      };

      // then stream calls
      then.call(
        result,
        function (nextResult) {
          _thenLock(function () {
            resolvePromise(promise, nextResult, resolve, reject);
          });
        },
        function (r) {
          // If you fail, you fail
          _thenLock(function () { reject(r); }); }); }catch (e) {
      _thenLock(function () { reject(e); }); }}else{ resolve(result); }}function Promise(fn) {
  if(! isObject(this)) {
    throw new TypeError("Promise must be the object instantiated by new.");
  }
  if(! isFunction(fn)) {throw new TypeError("The Promise constructor entry must be a function");
  }

  / / state pending/fulfilled/rejected
  this._status = "pending"; / / the default pending
  this._value = null; / / value
  this._error = null; / / exception

  // Successful callback
  this._onResolvedFns = [];
  // Failed callback
  this._onRejectedFns = [];

  try {
    // Bind the current context
    fn(resolve.bind(this), reject.bind(this));
  } catch(e) { reject(e); }}// The prototype method then returns a new promise to form a chain call
Promise.prototype.then = function (onResolved, onRejected) {
  // Then receives two functions. If they are not functions, the value of the previous THEN continues downward
  onResolved = isFunction(onResolved)
    ? onResolved
    : function (y) {
        return y;
      };
  onRejected = isFunction(onRejected)
    ? onRejected
    : function (err) {
        throw err;
      };

  var _this = this;

  var promise = new Promise(function (resolve, reject) {
    if (_this._status === "pending") {
      / / pending state
      // Save callback successfully
      _this._onResolvedFns.push(function () {
        setTimeout(function () {
          try {
            resolvePromise(promise, onResolved(_this._value), resolve, reject);
          } catch(error) { reject(error); }}); });// Store the failed callback
      _this._onRejectedFns.push(function () {
        setTimeout(function () {
          try {
            resolvePromise(promise, onRejected(_this._error), resolve, reject);
          } catch(error) { reject(error); }}); }); }else {
      setTimeout(function () {
        try {
          // this is a big pity/Rejected
          resolvePromise(
            promise,
            _this._status === "fulfilled"
              ? onResolved(_this._value)
              : onRejected(_this._error),
            resolve,
            reject
          );
        } catch(error) { reject(error); }}); }});return promise;
};

// The prototype method catch throws an exception
Promise.prototype.catch = function (onError) {
  // Catch method is short for then method failed
  return this.then(null, onError);
};

// The prototype method promises the last processing after a normal or an exception
Promise.prototype.finally = function (onFinally) {
  var _finally = Promise.resolve(onFinally());

  return this.then(
    function (value) {
      return _finally.then(function () {
        return value;
      });
    },
    function (error) {
      return _finally.then(function () {
        throwerror; }); }); };// interrupt to throw the result set
Promise.resolve = function (value) {
  return new Promise(function (resolve) {
    resolve(value);
  });
};

// interrupt to throw an exception
Promise.reject = function (reason) {
  return new Promise(function (resolve, reject) {
    reject(reason);
  });
};

// Returns the fastest result of multiple Promise collection requests
Promise.race = function (可迭代) {
  return new Promise(function (resolve, reject) {
    / / shallow copy
    var array = Array.prototype.slice.call(iterable);
    for (var i = 0; i < array.length; i++) {
      Promise.resolve(array[i]).then(resolve, reject); }}); };// Returns all normal or abnormal result sets from multiple Promise sets
Promise.allSettled = function (可迭代) {
  return new Promise(function (resolve, reject) {
    var array = Array.prototype.slice.call(iterable);
    if(! array.length)return resolve([]);

    var results = [];
    var count = 0;

    for (var i = 0; i < array.length; i++) {
      (function (i) {
        Promise.resolve(array[i]).finally(function (value) {
          results[i] = res;
          count++;

          if(count === array.length) { resolve(results); }}); })(i); }}); };// Return all normal result sets from multiple Promise sets
Promise.all = function (可迭代) {
  return new Promise(function (resolve, reject) {
    var array = Array.prototype.slice.call(iterable);
    if(! array.length)return resolve([]);

    var results = [];
    var count = 0;

    for (var i = 0; i < array.length; i++) {
      (function (i) {
        Promise.resolve(array[i])
          .then(function (res) {
            results[i] = res;
            count++;

            if (count === array.length) {
              resolve(results);
            }
          })
          .catch(function (error) { reject(error); }); })(i); }}); };module.exports = Promise;
Copy the code

To study the reference

  • Promise source code reference
  • Promise MDN
  • Ten questions that Promise will know and will know
  • Promises/A+ 100 lines of code
  • Hello JavaScript asynchronous programming – Understand the beauty of JavaScript asynchrony
  • 1. Write A Promise that Promises/A+ Promises from scratch
  • Promise implementation principle (source code attached)

summary

The above is the learning summary of promise, I hope to help you, if there is something wrong, I hope you don’t hesitate to correct, thank you.