This article mainly through the example of the use of promise, to realize a promise.

State initialization

Promise usage

let p = new Promise((res, rej) = > {
   // res('success')
   rej('err')})console.log(p1); 
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "success"
Copy the code

Promise initializes the implementation

First, make sure that a promise can be implemented with a class. When a promise is initialized, it needs to know the state of the promise and the return value of the promise.

In a promise, there are two properties, PromiseState and PromiseResult, which store the state and result of the promise respectively.

When a user passes a new Promise, they pass in a callback function that uses resolve and Rejected.

  • When you call resolve, change the state to depressing and assign a value to the result.
  • When you call REJECT, change the state to Rejected and assign a value to the result.

Initialization is as follows:

class MyPromise {
  constructor(cb) {
    this['[[PromiseState]]'] = 'pending'
    this['[[PromiseResult]]'] = undefined
    this.onResolved = [] The two collections are not currently in use
    this.onRejected = [] // Store the failed callback collection
    cb(this.#resolve.bind(this), this.#reject.bind(this))  // We need to bind this, otherwise we can't get it
  }
  #resolve(val) {
    this['[[PromiseState]]'] = 'fulfilled'
    this['[[PromiseResult]]'] = val
  }
  #reject(val) {
    this['[[PromiseState]]'] = 'rejected'
    this['[[PromiseResult]]'] = val
  }
}
Copy the code

If the cb call does not bind this to the promise internal private property, an error will be reported:The output of this is undefined, so we can’t get this, so we need to bind this when cb calls.

A static method

resolve

Promise.resolve() returns a resolved Promise object.

Method of use

console.log(Promise.resolve('success')); 
// Promise{
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: "success"
// }
Copy the code

implementation

Use the resolve static method directly and output the same result as new Promise((res, rej) => {res(‘success’)}). Therefore, when calling the resolve method, you need to instantiate a Promise class that calls res internally.

 static resolve(val) {
    return new MyPromise((res, rej) = > {
      res(val)
    })
  }
Copy the code

reject

Promise.reject() returns a rejected Promise object.

Method of use

console.log(Promise.reject('err')); 
// Promise{
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: "err"
// }

Copy the code

implementation

Using the resolve static method directly, the output is the same as new Promise((res, rej) => {rej(‘err’)}).

 static reject(val) {
    return new MyPromise((res, rej) = > {
      rej(val)
    })
  }
Copy the code

allSettled

Promise.allsettled () accepts an array of objects composed of the results of a fulfilled Promise (state: fulfilled or rejected).

AllSettled usage

let p2 = new Promise((res, rej) = > {
      setTimeout(() = > {
        res("p1");
      }, 2000);
    })
    let p3 = new Promise((res, rej) = > {
      setTimeout(() = > {
        rej("p2 err");
      }, 1000);
    })
    Promise.allSettled([p2, p3]).then(res= > {
      console.log(res); // [{status: "fulfilled", value: "p1"},{status: "rejected", reason: "p2 err"}]
    })
Copy the code

Implementation method

AllSettled receives an array of objects and outputs an array regardless of state. When the array loop calls THEN, you need to consider the return results of two states, and in each state, format the data returned from that item. For example, {status: “depressing “, value: “p1”}, the two attributes in success are status and value respectively.

At implementation time, you need to set a variable that stores the length of the new result, num. If num is equal to the length of the received array lists, it indicates that the uniform format has been changed and the next callback needs to be performed.

 static allSettled(lists) {
    let resArr = new Array(lists.length)
    let num = 0
    return new MyPromise((resolve) = > {
      lists.forEach((item, key) = > {
        let obj = {}
        item.then(
          (res) = > {
            obj['status'] = 'fulfilled'
            obj['value'] = res
            resArr[key] = obj
            num++
            if (num >= lists.length) {
              resolve(resArr)
            }
          },
          (err) = > {
            obj['status'] = 'rejected'
            obj['reason'] = err
            resArr[key] = obj
            num++
            if (num >= lists.length) {
              resolve(resArr)
            }
          },
        )
      })
    })
  }
Copy the code

all

Promise.all waits for everything to complete, and if there is an error in the array in all, an exception is executed.

All usage

let p2 = new Promise((res, rej) = > {
      setTimeout(() = > {
        res("p1");
      }, 2000);
    })
    let p3 = new Promise((res, rej) = > {
      setTimeout(() = > {
        res("p2");
      }, 1000);
    })
    Promise.all([p2, p3])
    .then(res= > {
      console.log(res);
    })
 // ["p1", "p2"]
Copy the code

All implementation

Like allSettled, you only need to consider the success case, but still need to determine if everything is executed.

  static all(lists) {
    let newArr = new Array(lists.length)
    let num = 0
    return new MyPromise((resolve) = > {
      lists.forEach((item, key) = > {
        item.then((res) = > {
          newArr[key] = res
          num++
          if (num >= lists.length) {
            resolve(newArr)
          }
        })
      })
    })
  }
Copy the code

race

Promise.race() executes whoever has a short execution time, regardless of state.

Race usage

let p1 = new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('success')},1000)})let p2 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject('failed')},500)})Promise.race([p1, p2]).then((result) = > {
  console.log(result)
}).catch((error) = > {
  console.log(error)  // 打开的是 'failed'
})
Copy the code

Race to achieve

When traversing lists, call then directly.

  static race(lists) {
    return new MyPromise((resolve, reject) = > {
      lists.forEach((item) = > {
        item.then(
          (res) = > {
            resolve(res)
          },
          (err) = > {
            reject(err)
          },
        )
      })
    })
  }
Copy the code

any

Promise.any() returns the first of the successful states.

Any usage

let p1 = new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('success')},1000)})let p2 = new Promise((resolve, reject) = > {
    setTimeout(() = > {
      reject('failed')},500)})Promise.any([p1, p2]).then((result) = > {
    console.log(result)
  }).catch((error) = > {
    console.log(error)
  })
 // success
Copy the code

Any implementation

Performs the first callback for the successful state. If none of the iterable promises succeeds (i.e. all Promises fail/reject), return an instance of the failed promise and AggregateError type.

  static any(lists) {
  return new MyPromise((resolve, reject) = > {
    let errs = []
    let len = lists.length
    if (len === 0) reject(new AggregateError('All promises were rejected'))
    lists.forEach(item= > {
      item.then(res= > {
        resolve(res)
      }, err= > {
        len--
        errs.push(err)
        if (len === 0) reject(new AggregateError(errs))
      })
    })
  })
}
Copy the code

Prototype method

Then prototype method

Promise.prototype.then() accepts up to two callbacks: those in the case of success and failure.

Basic then

Basic then usage

   let p = new Promise((res, rej) = > {
      res('success');
    })
   p.then(res= > {
        console.log(res); // success
    }, err= > {
      console.log(err);
    })
Copy the code

Implementation of basic THEN

Based on the basic usage of THEN in the previous paragraph, the RES or REJ executed in New Promise corresponds to the correct callback or error thrown, respectively. In then you can determine the state of the promise to perform different callbacks.

then(onFullfill, onReject) {
    if (this['[[PromiseState]]'= = ='fulfilled') {
      onFullfill(this['[[PromiseResult]]'])}if (this['[[PromiseState]]'= = ='rejected') {
      onReject(this['[[PromiseResult]]'])}}Copy the code

This is a big pity, which means success. If it is rejected, it indicates failure. When multiple THEN’s are invoked, the release callback is invoked based on the call in the Promise, and the corresponding callback from multiple THEN’s is also fired.

 let p = new MyPromise((res, rej) = > {
    res('success')}); p.then(res= > {
  console.log(res);
}, err= > {
  console.log('err', err);
})
p.then((res) = > {
 console.log(res, '2');
}, (rej) = > {
  console.log('err2');
})
// success
// success 2
Copy the code

Chain calls to THEN

The chained use of then

    let p1 = new Promise((res, rej) = > {
      res('success');
    })
    p1.then(res= > {
      return new Promise((res, rej) = > {
        res('success2')})},err= > {
      console.log(err);
    }).then(res= > {
      console.log('2', res); // 2 success2
    })
Copy the code

Chain implementation of THEN

A chained implementation is the equivalent of returning a promise. Based on the state of the previous promise, the corresponding state callback is performed in then, and so on. If a promise is returned from the previous THEN, the state in that THEN is ‘pending’.

  #resolve(val) {
    if (this['[[PromiseState]]']! = ='pending') return
    setTimeout(() = > {
      this['[[PromiseState]]'] = 'fulfilled'
      this['[[PromiseResult]]'] = val
      // this.onResolvedQueue.forEach((fn) => fn && fn(val))
      let cb;
      while(cb = this.onResolvedQueue.shift()) {
        cb(val)
      }
    })
  }
  #reject(err) {
    if (this['[[PromiseState]]']! = ='pending') return
    setTimeout(() = > {
      this['[[PromiseState]]'] = 'rejected'
      this['[[PromiseResult]]'] = err
      // this.onRejectedQueue.forEach((fn) => fn && fn(err))
      let cb;
      while(cb = this.onRejectedQueue.shift()) {
        cb(err)
      }
    })
  }

 then(onFullfill, onReject) {
    // Returns a new promise
    return new MyPromise((resolve, reject) = > {
      // Successfully executed function
      let onFullfillFn = (val) = > {
        let res = onFullfill && onFullfill(val)
        // Returns a new promise to perform the next callback
        res instanceof MyPromise ? res.then(resolve) : resolve(res)
      }
      // Failed to execute the function
      let rejectFn = (err) = > {
        onReject && onReject(err)
        reject(err)
      }
      this.onResolvedQueue.push(onFullfillFn)
      this.onRejectedQueue.push(rejectFn)
    })
  }
Copy the code

A maximum of two callback functions are accepted in then. In a successful callback, the incoming callback may be directly logical processing, or a new Promise object may need to be returned, so you need to determine the return type. If a failed function is passed in, the user failed callback is executed directly.

Catch prototype method

Promise.prototype. Catch () Catch the Rejected condition and reject it.

The usage of catch

let p = new Promise((res, rej) = > {
      rej('err')
})
p.catch((reason) = > {
  console.log(reason) // err
})
Copy the code

Catch implementation

Failed callback to execute THEN directly.

catch(cb) {
    this.then(undefined, cb)
  }
Copy the code

Finally prototype method

Promise. Prototype. Finally () being fulfiled or rejected, will perform the finally in the callback function.

The finally usage

let p = new Promise((res, rej) = > {
      res('success')
    })
    p.then((reason) = > {
      console.log(reason)
    }).finally(() = > {
      console.log('finally');
    })
// success finally
Copy the code

The finally realize

 finally(cb) {
    return this.then(cb, cb)
  }
Copy the code