Course objectives

  • Understand PromiseA+ specification
  • Implement a simple Promise

Knowledge points

  • Three states of Promise
  • Promise’s then method
  • resolvePromise
  • OnFulfilled and onRejected

Additional knowledge points

PromiseA+

  1. A PROMISE is an object or function that has a THEN method
  2. Thenable is an object or function that has a THEN method
  3. Value is the value of the promise state when it succeeds. The parameter to resolve includes various data types, including undefined/thenable, or promise
  4. The reason parameter is the reject parameter, which indicates the reason for rejecting the promise state
  5. An exception is an outlier thrown with a throw

specification

Three states

This is very depressing. There are three kinds of promise: pending, fulfilled or rejected. “Pending” is the initial state, and “fulfilled” and “rejected” are the final states

A promise must have a value when it is changed to a fulfilled state after being resolved. A promise must have a reason value when it is changed to a rejected state

This is very depressing. This is fulfilled fulfilled

pending -> reject(reason) -> rejected

then

  Promise.then(onFulfilled, onRejected)
Copy the code

Promise provides a THEN method to access the final result, either value or reason

  1. Parameter requirements

    1.1 onFullfilled and onRejected must be functions, otherwise they are ignored.

  2. OnFulfilled features

    2.1 onFulfilled is called when the promise becomes fulfilled. The parameter is value

    2.2 This promise should not be called until it becomes fulfilled

    2.3 It can be invoked only once

  3. OnRejected features

    3.1 Call onRejected with reason when promise becomes Rejected

    3.2 Promise should not be called until it becomes rejected

    3.3 Can be invoked only once

  4. OnFulfilled and onRejected should be implemented for microtasks with queueMicrotask.

  5. The THEN method can be called multiple times

    5.1 After the promise state becomes fulfilled, all onFulfilled callbacks need to be executed in the then order, that is, in the registration order

    5.2 When the Promise state changes to Rejected, all onRejected callbacks need to be executed in the then order, that is, in the registration order

  6. The return value

    promise2 = promise1.then(onFulfilled, onRejected)
    Copy the code

    This will be fulfilled fulfilled or onRejected. If the result is x, call resolvePromise

    6.2 If an exception is thrown during onFulfilled or onRejected, PromisE2 needs to be rejected

    If onFulfilled is not a function, promise2 triggers this function with the value of promise1

    If onRejected is not a function, promise2 triggers Promise1’s reason

  7. resolvePromise

    resolvePromise(promise2, x, resolve, reject)
    Copy the code

    7.1 Reject TypeError if PROMISe2 is equal to X

    7.2 If X is a promsie

    This is very depressing. If X is pending, the promise must remain pending until X becomes fulfilled or rejected.

    A. fulfilled B. fulfilled C. fulfilled D. fulfilled

    X is rejected, reject promise with the same reason.

    7.3 If x is an object or a function

    let then = x.then
    Copy the code

    Reject promise with eas the reason

    Then is a function, then.call(x, resolvePromiseFn, rejectPromise)

    The incoming parameter of resolvePromiseFn is y, and the resolvePromise(promise2, y, resolve, reject) is executed.

    The input of a rejectPromise is R, reject promise with R

    If both the resolvePromise and rejectPromise are called, the first call takes precedence and subsequent calls are ignored.

    If the then call throws an exception, the resolvePromise or rejectPromise has already been called, then ignore it. Otherwise reject promise with eas the reason

    If then is not a function, fulfill promise with x

    Code implementation

    // Define the promise class and its initial state
    const PENDING = "pending";
    const FULLFILLED = "fullfilled";
    const REJECTED = "rejected";
    
    class MyPromise {
      // Save the callback for success or failure of pending state
      FULLFILLED_CALLBACK_LIST = [];
      REJECTED_CALLBACK_LIST = [];
      _status = PENDING;
      constructor(fn) {
        this.status = PENDING;
        this.value = null;
        this.reason = null;
        try {
          fn(this.resolve.bind(this), this.reject.bind(this));
        } catch (e) {
          this.reject(e); }}resolve(value) {
        if (this.status == PENDING) {
          this.value = value;
          this.status = FULLFILLED; }}reject(reason) {
        if (this.status == PENDING) {
          this.reason = reason;
          this.status = REJECTED; }}get status() {
        return this._status;
      }
      set status(newStatus) {
        this._status = newStatus;
        switch (newStatus) {
          case FULLFILLED:
            {
              this.FULLFILLED_CALLBACK_LIST.forEach((callback) = > {
                callback(this.value);
              });
            }
            break;
          case REJECTED:
            {
              this.REJECTED_CALLBACK_LIST.forEach((callback) = > {
                callback(this.reason);
              });
            }
            break; }}This parameter is fulfilled, this parameter is fulfilled, and this parameter is rejected
      then(onFulfilled, onRejected) {
        // If it is not a function, ignore it. Ignore means return value or reason as is.
        const realOnFulfilled = this.isFunction(onFulfilled)
          ? onFulfilled
          : (value) = > {
              return value;
            };
        const realOnRejected = this.isFunction(onRejected)
          ? onRejected
          : (reason) = > {
              throw reason;
            };
    
        // then returns a promise as a whole
        This may be fulfilled fulfilled or onRejected may be fulfilled; // If onFulfilled or onRejected throws an exception e, promise2 must refuse to execute and return rejected e
        // If onFulfilled or onRejected returns a value x, then run the resolvePromise method
        const promise2 = new MyPromise((resolve, reject) = > {
          const fulfilledMicrotask = () = > {
            try {
              const x = realOnFulfilled(this.value);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch(e) { reject(e); }};const rejectedMicrotask = () = > {
            try {
              const x = realOnRejected(this.reason);
              this.resolvePromise(promise2, x, resolve, reject);
            } catch(e) { reject(e); }};switch (this.status) {
            case FULLFILLED:
              fulfilledMicrotask();
              break;
            case REJECTED:
              rejectedMicrotask();
              break;
            case PENDING:
              // Pending state is temporarily stored in the array, waiting for the state to change before calling back
              this.FULLFILLED_CALLBACK_LIST.push(fulfilledMicrotask);
              this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask);
              break; }});return promise2;
      }
    
      resolvePromise(promise2, x, resolve, reject) {
        // If promise2 and x point to the same object, refuse to execute newPromise with TypeError
        if (promise2 === x) {
          return reject(
            new TypeError("The promise and the return value are the same")); }if (x instanceof MyPromise) {
          // If x is a promise, continue to execute x. If you get a y, continue parsing y
          queueMicrotask(() = > {
            x.then((y) = > {
              this.resolvePromise(promise2, y, resolve, reject);
            }, reject);
          });
        } else if (typeof x == "object" || this.isFunction(x)) {
          // If x is an object or function
          if (x === null) {
            return resolve(x);
          }
          let then = null;
          try {
            then = x.then;
          } catch (e) {
            return reject(e);
          }
    
          // If then is a function
          if (this.isFunction(then)) {
            let called = false;
            try {
              then.call(
                x,
                (y) = > {
                  if (called) return;
                  called = true;
                  this.resolvePromise(promise2, y, resolve, reject);
                },
                (r) = > {
                  if (called) return;
                  called = true; reject(r); }); }catch (e) {
              if (called) return; reject(e); }}else{ resolve(x); }}else{ resolve(x); }}// Check whether it is a function
      isFunction(param) {
        return typeof param === "function"; }}Copy the code