1 Implement your own Promise class and bind this

class Yu {
  static PENDING = "pending";
  static FUIFILLED = "fulfilled";
  static REJECTED = "rejected";
  constructor(executor) {
    this.state = YU.PENDING;
    this.value = null;
    executor(this.resolve, this.reject);
  }
  resolve(value) {}
  reject(reason){}}let p = new Yu((resolve, reject) = > {
  resolve(1);
});
console.log(p);
Copy the code

An error will be reported if you do not bind this, because es6 classes enable strict mode by default. To avoid this error, use bind to bind this

executor(this.resolve.bind(this), this.reject.bind(this));
Copy the code

While resolve reject synchronously overwrite the previous state, we expect the state to change from Pending to Rejected or pending to depressing. The following is solved by state protection

State protection and handling of executor exception catching

State protection is a simple process: Resolve reject, reject

  resolve(value) {
    if (this.state === Yu.PENDING) {
      this.state = Yu.FUIFILLED;
      this.value = value; }}reject(reason) {
    if (this.state === Yu.PENDING) {
      this.state = Yu.REJECTED;
      this.value = reason; }}Copy the code

When we use it this way, there may be situations like this

new Yu((resolve, reject) = > {
  // Use an incorrect variable
  console.log(bbb);

  // Or actively throw an error
  throw 2222;
  //
});
Copy the code

We all need to do error handling for executors, internally using a try catch, and internally modifying the state inside the catch

try {
  executor(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
  this.reject(e);
}
Copy the code

That will do

3 then the basic pending depressing rejected

  • Promises/A+ The specification states that you must provide a then method that accepts two parameters, onFulfilled and onRejected,then(onFulfilled, onRejected)The first argument is a successful callback and the second argument is a failed callback
  • Both parameters are optional, meaning.then()Then value penetration, which returns the value inside the current Promise instance
  • This is a big pity. When ondepressing is a function, it can be called only when the current state is very depressing.
  • Ondepressing is a function, which must be called only when the current state is Rejected. This can not be called many times before.
  • Then can be called multiple times on the same Promise instance
  • Then must return a new Promise instancepromise2 = promise1.then(onFulfilled, onRejected)

Then is a prototypical method that takes two callback functions as arguments.


then(onFulfilled, onRejected) {
  // The first function passed in is executed and the value on the instance is passed to the current function
  onFulfilled(this.value);
}
Copy the code

This will be a big pity. This will be a big pity when resolve(‘success’) is called internally to save SUCCss on the value attribute of the instance and change the current state to the success state. This is a big pity. When executing the ondepressing method, pass this. Value, which is success, to ondepressing, so that when you call it again, output the parameter value, which is the argument, success.

let p = new Yu((resolve, reject) = > {
  resolve("success");
}).then(val= > {
  console.log(val); //success
});
Copy the code

But when we comment resolve, there should be no output, and there is still an output, so we can’t just come up and execute the successful method in then. Instead, we should execute it under certain circumstances. What happens? This is the fulfilled request. Similarly, the other two cases are also similar. The code is modified as follows:

then(onFulfilled, onRejected) {
  //1 Check todo for two parameters
  When and when are two passed functions executed?
  this.state === Yu.FUIFILLED && onFulfilled(this.value);
  this.state === Yu.REJECTED && onRejected(this.value);
  if (this.state === Yu.PENDING) {
    // Save the function temporarily because you don't know when it succeeds or fails
  }
  //3 Return a new Promise instance why do I need to return a new Promise instance? Because I can support chain calls then is a recursive function that calls itself every time so I can just return this the next time I use then by looking for the then method on the prototype chain
  // Can also implement the chain call? So why not return this directly instead of using a new constructor to get a new instance? Because you need to pass the last state
  // And the return cannot be the same as the last instance, if the same should give an error message
}
Copy the code

When called, notice that the then method takes two callback functions as arguments: the first is the successful function and the second is the failed function. If the first or second parameter is not passed, do not report the error, and then we need to do parameter processing in the then

then(onFulfilled, onRejected) {
  //1 Check the two parameters
  onFulfilled =
    typeof onFulfilled === "function" ? onFulfilled : () = > {};
  onRejected = typeof onRejected === "function" ? onRejected : () = > {};
  //2 execute only when the two functions passed in are corresponding states
  this.state === Yu.FUIFILLED && onFulfilled(this.value);
  this.state === Yu.REJECTED && onRejected(this.value);
  if (this.state === Yu.PENDING) {
    // Save the function temporarily because you don't know when it succeeds or fails}}Copy the code

When handling errors, catch exceptions as well. So you need to refine the error handling function to throw the exception itself

//1 Check the two parameters
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : () = > {};
onRejected =
  typeof onRejected === "function"
    ? onRejected
    : err= > {
        throw err;
      };
Copy the code

The result of the call could be something like this

let p = new Yu((resolve, reject) = > {
  resolve("success");
}).then(
  val= > {
    console.log(val);
  }
  // The second argument is not passed, because then is processed internally
  // reason => {
  // console.log(reason);
  // }
);
Copy the code

Or you could call it this way

let p = new Yu((resolve, reject) = > {
  reject("Failure");
}).then(
  // The first argument is not a function and does not return an error because then is handled internally
  null.reason= > {
    console.log(reason); });Copy the code

Another problem is that if there are mistakes in the “onFulfilled” and “onRejected”, we also need to deal with the “try catch” internally

//2 execute only when the two functions passed in are corresponding states
if (this.state === Yu.FUIFILLED) {
  try {
    onFulfilled(this.value);
  } catch (e) {
    onRejected(this.value); }}if (this.state === Yu.REJECTED) {
  try {
    onRejected(this.value);
  } catch (e) {
    onRejected(this.value); }}Copy the code

Another detail is, is the resovle in the New Promise executed synchronously or asynchronously? We can take a look at the normal promsie output order

let p2 = new Promise((resolve, reject) = > {
  console.log(1111);
  resolve("Success");
  console.log(2222);
}).then(val= > console.log(val));
Copy the code

So we’ll make resolve Reject asynchronous, which we can emulate with setTimeout

const fakeMicroTask = (cb, timer = 1000) = > setTimeout(cb, timer);

//2 execute only when the two functions passed in are corresponding states
if (this.state === Yu.FUIFILLED) {
  fakeMicroTask(() = > {
    try {
      onFulfilled(this.value);
    } catch (e) {
      onRejected(this.value); }}); }if (this.state === Yu.REJECTED) {
  fakeMicroTask(() = > {
    try {
      onRejected(this.value);
    } catch (e) {
      onRejected(this.value); }}); }Copy the code

If a new Promise calls resolve or reject asynchronously, let’s look at the normal Promise execution order:

let p2 = new Promise((resolve, reject) = > {
  console.log(1111);
  setTimeout(() = > {
    resolve("Success");
  }, 1000);
  console.log(2222);
}).then(val= > console.log(val));
console.log("first");
// Output sequence
/ / 1111
/ / 2222
//first
// Output succeeded after one second
Copy the code

So we’re going to handle resolve asynchronously, and we’re going to handle pending, because the state is pending at first, and it’s going to wait a second before it changes

const fakeMicroTask = (cb, timer = 1000) = > setTimeout(cb, timer);
class Yu {
  static PENDING = "pending";
  static FUIFILLED = "fulfilled";
  static REJECTED = "rejected";
  constructor(executor) {
    this.state = Yu.PENDING;
    this.value = null;
    this.callbacks = [];

    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (e) {
      this.reject(e); }}resolve(value) {
    if (this.state === Yu.PENDING) {
      this.state = Yu.FUIFILLED;
      this.value = value;

      this.callbacks.forEach(cbObj= >{ cbObj.onFulfilled(value); }); }}reject(reason) {
    if (this.state === Yu.PENDING) {
      this.state = Yu.REJECTED;
      this.value = reason;

      this.callbacks.forEach(cbObj= >{ cbObj.onRejected(reason); }); }}then(onFulfilled, onRejected) {
    //1 Check the two parameters
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : () = > {};
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : err= > {
            throw err;
          };
    //2 execute only when the two functions passed in are corresponding states
    if (this.state === Yu.FUIFILLED) {
      fakeMicroTask(() = > {
        try {
          onFulfilled(this.value);
        } catch (e) {
          onRejected(this.value); }}); }if (this.state === Yu.REJECTED) {
      fakeMicroTask(() = > {
        try {
          onRejected(this.value);
        } catch (e) {
          onRejected(this.value); }}); }if (this.state === Yu.PENDING) {
      // Save the function temporarily because you don't know when it succeeds or fails
      this.callbacks.push({ onFulfilled, onRejected }); }}}Copy the code

Handle pending status exceptions

// There is no error handling
if (this.state === Yu.PENDING) {
  // Save the function temporarily because you don't know when it succeeds or fails
  this.callbacks.push({
    onFulfilled,
    onRejected
  });
}

// Add error handling to pending state
if (this.state === Yu.PENDING) {
  // Save the function temporarily because you don't know when it succeeds or fails
  this.callbacks.push({
    onFulfilled: value= > {
      try {
        onFulfilled(value);
      } catch(err) { onRejected(err); }},onRejected: reason= > {
      try {
        onRejected(value);
      } catch(err) { onRejected(err); }}}); }Copy the code

What is the following output order? :

let p = new Yu((resolve, reject) = > {
  setTimeout(() = > {
    resolve("success");
    console.log(111);
  });
}).then(value= > console.log(value), reason= > console.log(reason));
console.log("first");
Copy the code

1 first 2 success 3 111

But the normal order of promise output is

1 first 2 111 3 success

So we’re going to make a change: make it asynchronous

resolve(value) {
  if (this.state === Yu.PENDING) {
    this.state = Yu.FUIFILLED;
    this.value = value;

    fakeMicroTask(() = > {
      this.callbacks.forEach(cbObj= >{ cbObj.onFulfilled(value); }); }); }}reject(reason) {
  if (this.state === Yu.PENDING) {
    this.state = Yu.REJECTED;
    this.value = reason;

    fakeMicroTask(() = > {
      this.callbacks.forEach(cbObj= >{ cbObj.onRejected(reason); }); }); }}Copy the code

4 then supports chained calls

So let’s call

let p = new Yu((resolve, reject) = > {
  setTimeout(() = > {
    resolve("success");
    console.log(111);
  });
  console.log(2222);
})
  .then(value= > console.log(value), reason= > console.log(reason))
  .then(val= > console.log(val));
Copy the code

Uncaught TypeError: Cannot read property 'then' of undefined

  • Chain call so you return a new promise

  • New promise, reject, reject does not affect then.

To implement chain calls:

then(onFulfilled, onRejected) {
//1 Check the two parameters
onFulfilled =
  typeof onFulfilled === "function" ? onFulfilled : () = > {};
onRejected =
  typeof onRejected === "function"
    ? onRejected
    : err= > {
        throw err;
      };
//3 Returns a new Promise instance
let newThen = new Yu((resolve, reject) = > {
  //2 execute only when the two functions passed in are corresponding states
  if (this.state === Yu.FUIFILLED) {
    fakeMicroTask(() = > {
      try {
        onFulfilled(this.value);
      } catch (e) {
        onRejected(this.value); }}); }if (this.state === Yu.REJECTED) {
    fakeMicroTask(() = > {
      try {
        onRejected(this.value);
      } catch (e) {
        onRejected(this.value); }}); }if (this.state === Yu.PENDING) {
    // Save the function temporarily because you don't know when it succeeds or fails
    this.callbacks.push({
      onFulfilled: value= > {
        try {
          onFulfilled(value);
        } catch(err) { onRejected(err); }},onRejected: reason= > {
        try {
          onRejected(value);
        } catch(err) { onRejected(err); }}}); }});return newThen;
}
Copy the code

Then value penetration problem handling

Parameter verification directly returnsthis. The value//1 Check the two parameters
onFulfilled =
  typeof onFulfilled === "function" ? onFulfilled : () = > this.value;
onRejected =
  typeof onRejected === "function" ? onRejected : () = > this.value;
Copy the code

Then added promise exception handling

then(onFulfilled, onRejected) {
          //1 Check the two parameters
          onFulfilled =
            typeof onFulfilled === "function" ? onFulfilled : () = > this.value;
          onRejected =
            typeof onRejected === "function" ? onRejected : () = > this.value;
          // onRejected =
          // typeof onRejected === "function"
          / /? onRejected
          // : err => {
          // throw err;
          / /};
          //3.1 Create a new instance
          let newPromiseInstance = new Yu((resolve, reject) = > {
            //2 execute only when the two functions passed in are corresponding states
            if (this.state === Yu.FUIFILLED) {
              fakeMicroTask(() = > {
                try {
                  let result = onFulfilled(this.value);
                  resolve(result);
                } catch(e) { reject(e); }}); }if (this.state === Yu.REJECTED) {
              fakeMicroTask(() = > {
                try {
                  let result = onRejected(this.value);
                  // Resolve is the value returned from then onRejected
                  resolve(result);
                } catch(e) { reject(e); }}); }if (this.state === Yu.PENDING) {
              // Save the function temporarily because you don't know when it succeeds or fails
              this.callbacks.push({
                onFulfilled: value= > {
                  try {
                    let result = onFulfilled(value);
                    resolve(result);
                  } catch(err) { reject(err); }},onRejected: reason= > {
                  try {
                    let result = onRejected(reason);
                    resolve(result);
                  } catch(err) { reject(err); }}}); }});Return a new Promise instance
          return newPromiseInstance;
        }
Copy the code

Then we add a try catch to the three states, reject, and pass the error in the catch

Is the value returned in then a normal value or a new Promise?

  • The state and result of the object are determined by the return value of the callback function
  • If the return value is a Promise object:
    • If the return value is success, the new Promise is success
    • The return value fails, and new Primise is a failure
  • If the return value is not a Promise object
    • The new Promsie is success, and its value is the return value

Ondepressing returns the normal value, Let result = ondepressing (this.value)) this if (result instanceof Yu) is a promise instance, which will be fulfilled later. Result. then(resolve, reject) is called and the resolve, reject method is passed to the outer then.

// Success inside
if (result instanceof Yu) {
  // result.then(
  // value => {
  // resolve(value);
  / /},
  // reason => {
  // // handles the return inside the rejection
  // reject(reason);
  / /}
  // );
  // Equivalent to;
  result.then(resolve, reject);
} else {
  // Return normal value to change state directly
  resolve(result);
}

// Even in failure
// Change pending
Copy the code

Now the then method

then(onFulfilled, onRejected) {
          //1 Check the two parameters
          onFulfilled =
            typeof onFulfilled === "function" ? onFulfilled : () = > this.value;
          onRejected =
            typeof onRejected === "function" ? onRejected : () = > this.value;
          // onRejected =
          // typeof onRejected === "function"
          / /? onRejected
          // : err => {
          // throw err;
          / /};
          //3.1 Create a new instance
          let newThen = new Yu((resolve, reject) = > {
            //2 execute only when the two functions passed in are corresponding states
            if (this.state === Yu.FUIFILLED) {
              fakeMicroTask(() = > {
                try {
                  let result = onFulfilled(this.value);
                  if (result instanceof Yu) {
                    // result.then(
                    // value => {
                    // resolve(value);
                    / /},
                    // reason => {
                    // // handles the return inside the rejection
                    // reject(reason);
                    / /}
                    // );
                    // Equivalent to;
                    result.then(resolve, reject);
                  } else {
                    // Return normal value to change state directlyresolve(result); }}catch (e) {
                  onRejected(this.value); }}); }if (this.state === Yu.REJECTED) {
              fakeMicroTask(() = > {
                try {
                  let result = onRejected(this.value);
                  if (result instanceof Yu) {
                    result.then(resolve, reject);
                  } else {
                    // Return normal value to change state directlyresolve(result); }}catch (e) {
                  onRejected(this.value); }}); }if (this.state === Yu.PENDING) {
              // Save the function temporarily because you don't know when it succeeds or fails
              this.callbacks.push({
                onFulfilled: value= > {
                  try {
                    let result = onFulfilled(value);
                    if (result instanceof Yu) {
                      result.then(resolve, reject);
                    } else {
                      // Return normal value to change state directlyresolve(result); }}catch(err) { onRejected(err); }},onRejected: reason= > {
                  try {
                    let result = onRejected(value);
                    if (result instanceof Yu) {
                      result.then(resolve, reject);
                    } else {
                      // Return normal value to change state directlyresolve(result); }}catch(err) { onRejected(err); }}}); }});Return a new Promise instance
          return newThen;
        }
Copy the code

A lot of this is repetitive, so you can take the same parts out and refactor the code

     parse(result, resolve, reject) {
          try {
            // Get rid of this line because success and failure call differently externally
            // let result = onFulfilled(this.value);
            if (result instanceof Yu) {
              result.then(resolve, reject);
            } else {
              // Return normal value to change state directlyresolve(result); }}catch (e) {
            onRejected(this.value); }}Copy the code

Now the whole thing looks like this:

const fakeMicroTask = (cb, timer = 1000) = > setTimeout(cb, timer);
class Yu {
  static PENDING = "pending";
  static FUIFILLED = "fulfilled";
  static REJECTED = "rejected";
  constructor(executor) {
    this.state = Yu.PENDING;
    this.value = null;
    this.callbacks = [];

    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (e) {
      this.reject(e); }}resolve(value) {
    if (this.state === Yu.PENDING) {
      this.state = Yu.FUIFILLED;
      this.value = value;

      fakeMicroTask(() = > {
        this.callbacks.forEach(cbObj= >{ cbObj.onFulfilled(value); }); }); }}reject(reason) {
    if (this.state === Yu.PENDING) {
      this.state = Yu.REJECTED;
      this.value = reason;

      fakeMicroTask(() = > {
        this.callbacks.forEach(cbObj= >{ cbObj.onRejected(reason); }); }); }}parse(result, resolve, reject) {
    try {
      // Get rid of this line because success and failure call differently externally
      // let result = onFulfilled(this.value);
      if (result instanceof Yu) {
        result.then(resolve, reject);
      } else {
        // Return normal value to change state directlyresolve(result); }}catch (e) {
      onRejected(this.value); }}then(onFulfilled, onRejected) {
          //1 Check the two parameters
          onFulfilled =
            typeof onFulfilled === "function" ? onFulfilled : () = > this.value;
          onRejected =
            typeof onRejected === "function" ? onRejected : () = > this.value;
          //3.1 Create a new instance
          let newPromiseInstance = new Yu((resolve, reject) = > {
            //2 execute only when the two functions passed in are corresponding states
            if (this.state === Yu.FUIFILLED) {
              fakeMicroTask(() = > {
                this.parse(onFulfilled(this.value), resolve, reject);
              });
            }

            if (this.state === Yu.REJECTED) {
              fakeMicroTask(() = > {
                this.parse(onRejected(this.value), resolve, reject);
              });
            }
            if (this.state === Yu.PENDING) {
              // Save the function temporarily because you don't know when it succeeds or fails
              this.callbacks.push({
                onFulfilled: value= > {
                  this.parse(onFulfilled(value), resolve, reject);
                },
                onRejected: reason= > {
                  this.parse(onRejected(reason), resolve, reject); }}); }});Return a new Promise instance
          return newPromiseInstance;
        }

let p = new Yu((resolve, reject) = > {
  resolve("success");
})
  .then(
    value= > {
      console.log("first then " + value);
      return "alex from firs then ";
    },
    reason= > console.log(reason)
  )
  .then(val= > console.log("second " + val));
Copy the code

Tests chained operations in THEN and returns normal values in THEN

Any initial Promise state change requires a manual call to Resove or Reject from New Promsie to allow the state to flow

  • Resolve: a successful callback in the first THEN returns the normal value. A successful callback in the second THEN prints the value returned in the first THEN

  • Case 2 reject a value, the first then it failed callback returns average value inside, in the second then the success callback to print first then inside the callback returns average value inside of failure

  • The first then successful callback returns a normal value, and the second then successful callback outputs the value returned by the previous THEN successful callback

  • Case 4 tests pending state, which calls reject with asynchrony. The first then failure callback returns a normal value, while the second then success callback outputs the value returned by the previous THEN failure callback

Then returns the processing of a promise

All of the above deals with cases where then returns the base value, and if we return a pormise, our current version is not correct, we have to do something about it, If the result is a promise instance, pass it to the then method. If it is a promise instance, pass it to the then method. If it is a promise instance, pass it to the then method. This is a big pity.

Simplify the code inside

So I’m going to write it this way

Then rejected and pending return Promise

Rejected before renovation:

Rejected after modification:

Before modifying pending cases, handle the two cases separately:

Pending after modification:

Now the then method:

 then(onFulfilled, onRejected) {
          //1 Check the two parameters
          onFulfilled =
            typeof onFulfilled === "function" ? onFulfilled : () = > this.value;
          onRejected =
            typeof onRejected === "function" ? onRejected : () = > this.value;

          //3.1 Create a new instance
          let newPromiseInstance = new Yu((resolve, reject) = > {
            //2 execute only when the two functions passed in are corresponding states
            
            // Successful judgment and handling
            if (this.state === Yu.FUIFILLED) {
              // put it in an asynchronous queue
              fakeMicroTask(() = > {
                // Add error handling
                try {
                  // Process the return value of then onFulfilled is an ordinary value or a new Promise
                  // Get the return value
                  let result = onFulfilled(this.value);
                  // The return value is an instance of Promise
                  if (result instanceof Yu) {
                    result.then(resolve, reject);
                  } else {
                  // Return a value that is not an instance of Promiseresolve(result); }}catch (e) {
                  // The fault-tolerant processing of the onFulfilled or onRejected function will be received in the onRejected function of the next THENreject(e); }}); }// Failure judgment and handling
            if (this.state === Yu.REJECTED) {
              fakeMicroTask(() = > {
                try {
                 // Then onRejected returns a normal value or a new Promise
                  let result = onRejected(this.value);
                  if (result instanceof Yu) {
                    result.then(resolve, reject);
                  } else{ resolve(result); }}catch(e) { reject(e); }}); }if (this.state === Yu.PENDING) {
              // Save the function temporarily because you don't know when it succeeds or fails
              this.callbacks.push({
                onFulfilled: value= > {
                  try {
                  // Then pending ondepressing returns a normal value or a new Promise
                    let result = onFulfilled(this.value);
                    if (result instanceof Yu) {
                      result.then(resolve, reject);
                    } else{ resolve(result); }}catch(e) { reject(e); }},onRejected: reason= > {
                  try {
                   // Then pending onRejected returns a normal value or a new Promise
                    let result = onRejected(this.value);
                    if (result instanceof Yu) {
                      result.then(resolve, reject);
                    } else{ resolve(result); }}catch(e) { reject(e); }}}); }});Return a new Promise instance
          return newPromiseInstance;
        }

Copy the code

Small refactoring of then methods

There are too many similar contents in the try catch of the above three states. We can encapsulate a small function for unified processing:

parse(result, resolve, reject) {
  try {
    // Get rid of this line because success and failure call differently externally
    // let result = onFulfilled(this.value);
    if (result instanceof Yu) {
      //then calls resolve reject when a successful or failed callback returns a promise instance
      result.then(resolve, reject);
    } else {
      // Return normal value to change state directlyresolve(result); }}catch (e) {
    reject(this.value); }}Copy the code

Remove comments:

parse(result, resolve, reject) {
  try {
    if (result instanceof Yu) {
      result.then(resolve, reject);
    } else{ resolve(result); }}catch (e) {
    reject(this.value); }}Copy the code

Next, replace the repeated parts as a parse function

A modified test of then that returns a promise and resolve state

Test for cases where a promise is returned from then and written incorrectly on purpose

What is the output of resovle if you place an intentionally miswritten line after it? The processing of native Primise goes like this

The result is: no processing, it will be processed before

We implemented Yu as well:

Similarly, use the parse function and continue to modify the repeats in the pending and onRejected states of then.

After the rejected:

The onRejected callback returns a new promise from the first then parameter, which is the onRejected callback

Pending state before transformation:

Modified:

Test the case for Pending Resolve

Test pending Reject

The whole then method after modifying pending:

The code is much smaller and more readable than the previous THEN method

The native Promise doesn’t handle errors in an asynchronous call to resolve, which we’ll leave out for now.

Promise returns a constraint of type

If promise and x refer to the same object, reject promise with a TypeError as the reason.

Native Promise performance:

The method in then executes asynchronously and since it is asynchronous you can get the new PRImise instance P returned by THEN

There is no processing in our own implementation, and the output looks like this:

This can be done within the parse function:

   parse(newReturnedPromiseInstance, result, resolve, reject) {
          //If promise and x refer to the same object, reject promise with a TypeError as the reason.
          if (newReturnedPromiseInstance === result) {
            throw new TypeError("Chaining cycle detected for promise");
          }
          try {
            if (result instanceof Yu) {
              result.then(resolve, reject);
            } else{ resolve(result); }}catch (e) {
            reject(this.value); }}Copy the code

Then make changes in then where parse is called:

Test it out and it works

5 implementation Promise. Resolve

Js es6 plus static means to define a method or property on a constructor, which you can use as a constructor. Resolve class Yu{static resolve(value) {}

The resolve Reject of the native Promise passes in a base value

Our implementation:

static resolve(value) {
  return new Yu((resolve, reject) = > {
    resolve(value);
  });
}
Copy the code

Also consider the case where a promise object is passed in, so the code changes to:

static resolve(value) {
  return new Yu((resolve, reject) = > {
    if (value instanceof Yu) {
      value.then(resolve, reject);
    } else{ resolve(value); }}); }Copy the code

Test Yu. Resolve

6 realization Promise. Reject

In the same way Yu. Reject

static reject(reason) {
  return new Yu((resolve, reject) = > {
    if (reason instanceof Yu) {
      reason.then(resolve, reject);
    } else{ reject(reason); }}); }Copy the code

Test Yu. Reject

7 realization Promise. All

The idea is to return a new Promise and reject any failure, which is a direct then failure,

All successes are successful, so an array count is required to see if the result array is the same as the primises array passed in. If so, all results are passed as arguments to resolve

Code implementation

static all(promisesArr) {
  let values = [];
  return new Yu((resolve, reject) = > {
    promisesArr.forEach(promise= > {
      promise.then(
        val= > {
          values.push(val);
          // resolve if it is equal
          if(values.length == promisesArr.length) { resolve(values); }},reason= >{ reject(reason); }); }); }); }Copy the code

There is one failure of the test

All tests are successful

8 implementation Promise. Race

Use whoever is quick

static race(promsisesArr) {
  return new Yu((resolve, reject) = > {
    promsisesArr.map(promise= > {
      promise.then(
        value= > {
          resolve(value);
        },
        reason= >{ reject(reason); }); }); }); }Copy the code

9 test todo after fill

Summary:

  • Yu is a constructor,
    • This is a pity rejected, which is the current state. The default is pending
    • There is also a this.value that holds the value passed in externally
    • The executor callback takes resolve, reject, and error handling. The executor callback takes resolve, reject, and reject.
    • Executors are called in the constructor and are synchronized
    • There is also a callbacks function on top of the constructor, which is used to hold the onFulfilled and onRejected functions, so that the resolve or reject functions can be called later
    • A promise state change starts with a manual call to resolve or reject, both synchronous and asynchronous
  • Yu’s prototype had the Resovle Reject THEN method on it
    • The resolve method does three things:
      1. When the current state is pending, store the incoming value on this.value
      2. When the current state is pending, modify the current state as depressing
      3. If there is a value in the callbacks on the current instance, loop through it, retrieve the ondepressing function inside, execute it, and pass the resolve parameter to it
    • The reject method does three things:
      1. When the current state is pending, store incoming reason on this.value
      2. When the current status is Pending, change the current status to Rejected
      3. If the current instance has a value in the callbacks, loop through it, fetch the onRejected function, execute it, and pass resolve to it
    • Then method
      1. Return a new Promise instance that continues using the method on the prototype chain through the prototype chain

      2. Two arguments to then are processed

        • Handling errors
        • Handles cases where arguments are not functions
        • Handle the case where the second argument throws an exception
      3. There are three types of then that handle 3.1 Pending states. The resolve and REJECT callbacks are used for pending states. Execute with the loop callbacks

        -3.1.1 Ondepressing is uncertain about the current state, so it will be saved to the previous callbacks. After the ondepressing return value is fulfilled, try catch will process it and determine whether the return value is an instance of Promise. If so, the then method of the result will be called. -3.1.2 onRejected = resolve,reject = reject If so, call the result's then method, pass resolve,reject, or resolve.Copy the code

        3.2 The state of pity

        - In asynchrony, call resolveCopy the code

        3.3 Rejected status

        Put it in async, call RejectCopy the code
      4. A new promise returned in then cannot be processed in the next THEN, and the previous instance can only be fetched in asynchrony

      5. Error handling of three states

      6. Over-extraction of duplicate code

  • The Yu constructor has the All Race Resolve Reject method above it
    • All: If there is one failure, the system fails. If there are multiple successes, the system succeeds. If there are multiple successes, an array is maintained internally. Each promise.then is passed a success value into the array. Finally, the two arrays are judged to be equal in length, which means success, and an array with all asynchronous success values is returned
    • Race: One failure is a failure, one success is a change of status to success.
    • resolve:
      • The case where the parameter passed is the base value
      • The argument passed in is a case of a Promise instance
    • Resolve:
      • The case where the parameter passed is the base value
      • The argument passed in is a case of a Promise instance
    • There are still some details that are not perfect in the implementation process, for example, some more detailed boundary processing is not implemented here, welcome to correct,

The complete code

<! DOCTYPE html><html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
   <meta http-equiv="X-UA-Compatible" content="ie=edge" />
   <title>Document</title>
 </head>
 <body>
   <script>
     const fakeMicroTask = (cb, timer = 1000) = > setTimeout(cb, timer);
     class Yu {
       static PENDING = "pending";
       static FUIFILLED = "fulfilled";
       static REJECTED = "rejected";
       constructor(executor) {
         this.state = Yu.PENDING;
         this.value = null;
         this.callbacks = [];

         try {
           executor(this.resolve.bind(this), this.reject.bind(this));
         } catch (e) {
           this.reject(e); }}resolve(value) {
         if (this.state === Yu.PENDING) {
           this.state = Yu.FUIFILLED;
           this.value = value;

           fakeMicroTask(() = > {
             this.callbacks.forEach(cbObj= >{ cbObj.onFulfilled(value); }); }); }}reject(reason) {
         if (this.state === Yu.PENDING) {
           this.state = Yu.REJECTED;
           this.value = reason;

           fakeMicroTask(() = > {
             this.callbacks.forEach(cbObj= >{ cbObj.onRejected(reason); }); }); }}parse(newReturnedPromiseInstance, result, resolve, reject) {
         //If promise and x refer to the same object, reject promise with a TypeError as the reason.
         if (newReturnedPromiseInstance === result) {
           throw new TypeError("Chaining cycle detected for promise");
         }
         try {
           if (result instanceof Yu) {
             result.then(resolve, reject);
           } else{ resolve(result); }}catch (e) {
           reject(this.value); }}then(onFulfilled, onRejected) {
         //1 Check the two parameters
         onFulfilled =
           typeof onFulfilled === "function" ? onFulfilled : () = > this.value;
         onRejected =
           typeof onRejected === "function" ? onRejected : () = > this.value;
         //3.1 Create a new instance
         let newPromiseInstance = new Yu((resolve, reject) = > {
           //2 execute only when the two functions passed in are corresponding states
           if (this.state === Yu.FUIFILLED) {
             fakeMicroTask(() = > {
               this.parse(
                 newPromiseInstance,
                 onFulfilled(this.value),
                 resolve,
                 reject
               );
             });
           }
           if (this.state === Yu.REJECTED) {
             fakeMicroTask(() = > {
               this.parse(
                 newPromiseInstance,
                 onRejected(this.value),
                 resolve,
                 reject
               );
             });
           }
           if (this.state === Yu.PENDING) {
             // Save the function temporarily because you don't know when it succeeds or fails
             this.callbacks.push({
               onFulfilled: value= > {
                 this.parse(
                   newPromiseInstance,
                   onFulfilled(this.value),
                   resolve,
                   reject
                 );
               },
               onRejected: reason= > {
                 this.parse(
                   newPromiseInstance,
                   onRejected(this.value), resolve, reject ); }}); }});Return a new Promise instance
         return newPromiseInstance;
       }

       static resolve(value) {
         return new Yu((resolve, reject) = > {
           if (value instanceof Yu) {
             value.then(resolve, reject);
           } else{ resolve(value); }}); }static reject(reason) {
         return new Yu((resolve, reject) = > {
           if (reason instanceof Yu) {
             reason.then(resolve, reject);
           } else{ reject(reason); }}); }static all(promisesArr) {
         let values = [];
         return new Yu((resolve, reject) = > {
           promisesArr.forEach(promise= > {
             promise.then(
               val= > {
                 values.push(val);
                 // resolve if it is equal
                 if(values.length == promisesArr.length) { resolve(values); }},reason= >{ reject(reason); }); }); }); }static race(promsisesArr) {
         return new Yu((resolve, reject) = > {
           promsisesArr.map(promise= > {
             promise.then(
               value= > {
                 resolve(value);
               },
               reason= >{ reject(reason); }); }); }); }}/ / test Yu. All
     let p1 = new Yu(resolve= > {
       resolve(1);
     });
     let p2 = new Yu((resolve, reject) = > {
       reject("Reject");
     });
     Yu.all([p1, p2]).then(
       arrs= > {
         console.log(arrs);
       },
       err= > {
         console.log(err); });// // tests yu.resolve for passing base values
     // Yu.resolve("test").then(val => {
     // console.log(val);
     // });
     // // tests yu.resolve passed to promsie
     // let p = new Yu((resolve, reject) => {
     // resolve("yu succeed ");
     // });
     // Yu.resolve(p).then(val => {
     // console.log(val);
     // });
     // console.log(
     // Yu.reject("reject Yu ").then(null, reason => {
     // console.log(reason);
     / /})
     // );

     // Test yu.reject for passing base values
     // Yu.reject("test").then(null, val => {
     // console.log(val);
     // });
     // // tests yu.reject for passing promsie
     // let p = new Yu((resolve, reject) => {
     //   reject("yu  拒绝");
     // });
     // Yu.reject(p).then(null, err => {
     // console.log(err);
     // });

     //test diy Promsie
     // let p = new Yu((resolve, reject) => {
     // setTimeout(() => {
     // reject(" reject ");
     / /}, 1000);
     // })
     // .then(
     // value => {
     // console.log(value);
     // return new Yu((resolve, reject) => {
     // resolve("Yu first then resolve");
     / /});
     / /},
     // reason => {
     // console.log(reason);
     // return new Yu((resolve, reject) => {
     // console.log(aaaa);
     // resolve("Yu first then reject");
     / /});
     / /}
     / /)
     // .then(
     // val => console.log(val),
     // reason => {
     // console.log("second then rejected function " + reason);
     / /}
     / /);

     //test chain promise
     // let p1 = new Yu(resolve => {
     // resolve(222);
     // });
     // let p2 = p1.then(val => {
     // return p2;
     // });
     // let p = new Promise((resolve, reject) => {
     // resolve("111");
     // });
     // let p2 = p.then(val => {
     // return p2;
     // });
     // console.log(p2);

     //test promise
     // let p2 = new Promise((resolve, reject) => {
     // setTimeout(() => {
     // console.log(AAA);
     // resolve("success");
     / /}, 1000);
     // })
     // .then(
     // val => {
     // console.log(val);
     // return new Promise((resolve, reject) => {
     // resolve("first then resolve");
     / /});
     / /},
     // reason => {}
     / /)
     // .then(
     // val => {
     // console.log(val);
     / /},
     // reason => {
     // console.log(reason);
     / /}
     / /);

     //test Promsie.resolve Promise.reject
     // let p1 = new Promise(resolve => {
     // resolve(" resolve ");
     // });
     // Promise.resolve(p1).then(val => {
     // console.log(val);
     // });
     // let p2 = new Promise((resolve, reject) => {
     // reject(" reject ");
     // });
     // Promise.reject(p2).then(
     // val => {
     // console.log(val);
     / /},
     // reason => {
     // console.log(reason);
     / /}
     // );

     // test Promise.all
     // let p11 = new Promise(resolve => {
     // resolve(111);
     // });
     // let p22 = new Promise((resolve, reject) => {
     // // reject(2);
     // resolve(222);
     // });
     // Promise.all([p11, p22]).then(
     // arrs => {
     // console.log(arrs);
     / /},
     // err => {
     // console.log(err);
     / /}
     // );
   </script>
 </body>
</html>

Copy the code

Thank you

  • Can see here, that is really read carefully, thank you!