In our work, promises are used to solve asynchronous callbacks. A lot of libraries or plug-ins that I use usually use promise, such as AXIos, FETCH, etc. But do you know how a promise is written?

PromisesA + (promisesA+) (promisesA+) (promisesA+) (promisesA+)

1. Promise’s statement

First of all, a promise must be a class, so we declare it as a class.

  • Due to thenew Promise((resolve, reject)=>{}), so pass a parameter (function),secretThey call it Executor, pass it in, execute it.
  • Executor takes two arguments, called resolve and reject.
  • Because resolve and Reject are executable, they are both functions that we declare with a let.
class Promise{
  / / the constructor
  constructor(executor){
    / / success
    let resolve = (a)= >{};/ / fail
    let reject = (a)= >{};// Execute immediatelyexecutor(resolve, reject); }}Copy the code

Resolve the base state

secretTo have a rule on a Promise:

  • This is very depressing. There are three states: state pending, fulfilled and rejected

  • This is very depressing. Pending is the initial state and can be changed to fulfilled or rejected.

  • Upon success, there must be no transition to another state, and there must be an immutable value

  • On failure, no change to another state must occur, and there must be an immutable reason.

  • New Promise((resolve, reject)=>{resolve(value)}) The resolve parameter is successful. The value parameter is received. The state changes to fulfilled and cannot be changed again.

  • New Promise((resolve, reject)=>{reject(reason)}) {reject(resolve)=>{reject(reason)})

  • Reject () if an error occurs in the executor function.

So we get the following code

class Promise{
  constructor(executor){
    // Initialize the state to wait
    this.state = 'pending';
    // The value of success
    this.value = undefined;
    // Cause of failure
    this.reason = undefined;
    let resolve = value= > {
      // If state changes, the resolve call will fail
      if (this.state === 'pending') {
        // After the resolve call, state is converted to the successful state
        this.state = 'fulfilled';
        // Store the successful value
        this.value = value; }};let reject = reason= > {
      // Reject calls fail when state is changed
      if (this.state === 'pending') {
        // Reject after the call, state is converted to the failed state
        this.state = 'rejected';
        // The cause of the storage failure
        this.reason = reason; }};// Reject is rejected if an error occurs
    try{
      executor(resolve, reject);
    } catch(err) { reject(err); }}}Copy the code

Then method

secretThis will be fulfilled someday. This method is called “then”, which has two parameters: onFulfilled,onRejected, a successful value and a failed reason

  • This is fulfilled. If the state is fulfilled, onFulfilled is executed, passing this.value. If state is rejected, then onRejected is executed, passing this.reason
  • If they are functions, they must be called after fulfilled, rejected, respectively, with value or reason as their first argument
class Promise{
  constructor(executor){... }// This method has two parameters: onFulfilled onRejected
  then(onFulfilled,onRejected) {
    This is fulfilled. Perform onFulfilled and pass in the successful value
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    };
    // If the status is rejected, execute onRejected and pass in the failure reason
    if (this.state === 'rejected') {
      onRejected(this.reason); }; }}Copy the code

This is the beginning of martial arts, can deal with the river’s lake small miscellaneous hair, but for the bandit with setTimeout or no.

Solving asynchronous implementations

We can do simple synchronization code now, but when resolve is executed in a setTomeout and then state is still pending, we need to store success and failure in a separate array when we call THEN, and call them once either reject or resolve

Similar to publish/subscribe, we store the two functions in the THEN. Since a promise can have multiple THEN’s, they are stored in the same array.

// Multiple then cases
let p = new Promise(a); p.then(); p.then();Copy the code

ForEach calls them on success or failure

class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    // The array that was successfully stored
    this.onResolvedCallbacks = [];
    // Failed to store the method array
    this.onRejectedCallbacks = [];
    let resolve = value= > {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        // Once resolve executes, call the function on the successful array
        this.onResolvedCallbacks.forEach(fn= >fn()); }};let reject = reason= > {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        // Once reject is executed, call the failed array function
        this.onRejectedCallbacks.forEach(fn= >fn()); }};try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled,onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    };
    if (this.state === 'rejected') {
      onRejected(this.reason);
    };
    // When the state is pending
    if (this.state === 'pending') {
      // this will be fulfilled
      this.onResolvedCallbacks.push((a)= >{
        onFulfilled(this.value);
      })
      // onRejected is passed into the failure array
      this.onRejectedCallbacks.push((a)= >{
        onRejected(this.reason); })}}}Copy the code

Resolve chained calls

We use it a lotnew Promise().then().then(), this is the chained call to solve callback hell

1. To achieve the chain, we return a promise in the first then by default. Promise2 = New Promise((resolve, reject)=>{})

  • Pass the value returned by promisE2 to the next THEN
  • If an ordinary value is returned, the ordinary value is passed to the next THEN

2, when we return an argument in the first THEN This return will produce a new promise which is the value of onFulfilled() or onRejected()

The secret specifies the value of onFulfilled() or onRejected(), the first value returned by then, which is called x, and the function that determines x is called resolvePromise

  • First, see if X is a promise.
  • If it is a Promise, take its result as the successful result of the new Promise2
  • If it is a common value, it is used as a successful result of PromisE2
  • So compare X to PROMISe2
  • The resolvePromise parameters are promise2 (the promise returned by default) and x (ourselves)return), resolve, reject
  • Resolve and reject are in Promise2
class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = value= > {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach(fn= >fn()); }};let reject = reason= > {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn= >fn()); }};try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled,onRejected) {
    // Declare the returned promise2
    let promise2 = new Promise((resolve, reject) = >{
      if (this.state === 'fulfilled') {
        let x = onFulfilled(this.value);
        // The resolvePromise function deals with the return promise and the default promise2
        resolvePromise(promise2, x, resolve, reject);
      };
      if (this.state === 'rejected') {
        let x = onRejected(this.reason);
        resolvePromise(promise2, x, resolve, reject);
      };
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push((a)= >{
          let x = onFulfilled(this.value);
          resolvePromise(promise2, x, resolve, reject);
        })
        this.onRejectedCallbacks.push((a)= >{
          let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); }}}));// Return promise to complete the chain
    returnpromise2; }}Copy the code

Complete the resolvePromise function

The cheater specifies a piece of code that allows different Promise codes to be interchanged, called a resolvePromise

  • If x === promise2; if x == promise2; if x == promise2
let p = new Promise(resolve= > {
  resolve(0);
});
var p2 = p.then(data= > {
  // Loop reference, wait for yourself to complete, lifetime can not complete
  return p2;
})
Copy the code

1, judge x

  • Otherwise, if x is an object or function,Let then be x.then
  • X cannot be null
  • So x is a normal value so resolve(x)
  • X is an object or function (including promise),let then = x.then2, when x is an object or function (default promise)
  • The statement then
  • Reject () reject() reject() reject()
  • If then is a function, then we use call to execute then, taking this as the first argument, followed by successful and failed callbacks
  • 3. Only one success and failure call can be made, so set a called to prevent multiple calls
function resolvePromise(promise2, x, resolve, reject){
  // Circular reference error
  if(x === promise2){
    / / reject an error
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  // Prevent multiple calls
  let called;
  // x is not null and x is an object or function
  if(x ! =null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // A+ specify, declare then = x
      let then = x.then;
      // If then is a function, the default is promise
      if (typeof then === 'function') { 
        // Let then execute the first argument with this followed by a successful callback and a failed callback
        then.call(x, y => {
          // Only one success or failure call can be made
          if (called) return;
          called = true;
          // The result of resolve is still a promise
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          // Only one success or failure call can be made
          if (called) return;
          called = true;
          reject(err);// Failure is failure})}else {
        resolve(x); // Direct success}}catch (e) {
      // This is also a failure
      if (called) return;
      called = true;
      // If the "then" error occurs, do not continue the executionreject(e); }}else{ resolve(x); }}Copy the code

Solve other problems

This is very depressing. If they are not functions, they must be ignored

  • OnFulfilled returns a normal value, which is directly equal to on successvalue => value
  • This is very depressing, this is very depressing, this is very depressing, this is very depressing, this is very depressing, this is very depressing, this is very depressingreason => throw err2,secretSpecifies that onFulfilled or onRejected cannot be called synchronously and must be called asynchronously. Let’s use setTimeout to solve the asynchronous problem
  • This is very depressing. If onFulfilled or onRejected, return reject()
class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = value= > {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach(fn= >fn()); }};let reject = reason= > {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn= >fn()); }};try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled,onRejected) {
    This function will be fulfilled fulfilled. If this function is not fulfilled, omit onFulfilled and return value
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value;
    // if onRejected is not a function, you can ignore onRejected and throw error
    onRejected = typeof onRejected === 'function' ? onRejected : err= > { throw err };
    let promise2 = new Promise((resolve, reject) = > {
      if (this.state === 'fulfilled') {
        / / asynchronous
        setTimeout((a)= > {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) { reject(e); }},0);
      };
      if (this.state === 'rejected') {
        / / asynchronous
        setTimeout((a)= > {
          // If an error occurs
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) { reject(e); }},0);
      };
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push((a)= > {
          / / asynchronous
          setTimeout((a)= > {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) { reject(e); }},0);
        });
        this.onRejectedCallbacks.push((a)= > {
          / / asynchronous
          setTimeout((a)= > {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) { reject(e); }},0)}); }; });// Return promise to complete the chain
    returnpromise2; }}Copy the code

You’re done

class Promise{
  constructor(executor){
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = value= > {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach(fn= >fn()); }};let reject = reason= > {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn= >fn()); }};try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled,onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value= > value;
    onRejected = typeof onRejected === 'function' ? onRejected : err= > { throw err };
    let promise2 = new Promise((resolve, reject) = > {
      if (this.state === 'fulfilled') {
        setTimeout((a)= > {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) { reject(e); }},0);
      };
      if (this.state === 'rejected') {
        setTimeout((a)= > {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch(e) { reject(e); }},0);
      };
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push((a)= > {
          setTimeout((a)= > {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) { reject(e); }},0);
        });
        this.onRejectedCallbacks.push((a)= > {
          setTimeout((a)= > {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch(e) { reject(e); }},0)}); }; });return promise2;
  }
  catch(fn){
    return this.then(null,fn); }}function resolvePromise(promise2, x, resolve, reject){
  if(x === promise2){
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  let called;
  if(x ! =null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      let then = x.then;
      if (typeof then === 'function') { 
        then.call(x, y => {
          if(called)return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          if(called)return;
          called = true; reject(err); })}else{ resolve(x); }}catch (e) {
      if(called)return;
      called = true; reject(e); }}else{ resolve(x); }}/ / resolve method
Promise.resolve = function(val){
  return new Promise((resolve,reject) = >{
    resolve(val)
  });
}
/ / reject method
Promise.reject = function(val){
  return new Promise((resolve,reject) = >{
    reject(val)
  });
}
/ / race method
Promise.race = function(promises){
  return new Promise((resolve,reject) = >{
    for(let i=0; i<promises.length; i++){ promises[i].then(resolve,reject) }; })}//all (get all promises, execute then, put the results in an array, return them together)
Promise.all = function(promises){
  let arr = [];
  let i = 0;
  function processData(index,data){
    arr[index] = data;
    i++;
    if(i == promises.length){
      resolve(arr);
    };
  };
  return new Promise((resolve,reject) = >{
    for(let i=0; i<promises.length; i++){ promises[i].then(data= >{
        processData(i,data);
      },reject);
    };
  });
}
Copy the code

How to verify that our promise is correct

1. Add the following code to the end first

NPM I promises-aplus-tests -g will make promises to MAC users with sudo in front of them

Promises -aplus-tests [js file name] promises-aplus-tests [js file name

// The current is through his test he will test a subject
/ / syntactic sugar
Promise.defer = Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve,reject) = >{
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
}
module.exports = Promise;
// NPM install Promises -aplus-tests to test if promises do not comply with promisesA+ specifications
Copy the code