preface

It’s been more than 4 months since xiao Bao wrote the article. In February, he didn’t find the state. He just precipitated himself and reflected on the writing process in the previous few months. So the package decided to put things right, and the rest of the articles will focus on JavaScript or ES6 for the time being, writing features or uses that the package thinks are important, new, or unique to the package.

At the heart of this week’s article is promises, which you’re probably all familiar with. Promises are a solution to asynchronous programming that is widely used in everyday programming. This week’s package will be written around the Promise source code handwriting. The initial plan for the source code handwriting is to implement three articles — the handwritten Promise Foundation, the handwritten Promise resolvePromise, and the handwritten Promise Static method.

Promises/A+ are Promises, so the Promise handwritten series will follow the Promises/A+ paradigm, through case studies and questions, to deliver Promises step by step.

By studying this article, you will learn:

  • 🌟 understandPromise A+specification
  • 🌟 Understand what isPromiseValues of penetration,PromiseChain call mechanism,PromiseRegister multiplethenMethods, etc.
  • 🌟 masterPromiseSource code preparation process
  • 🌟 master the publish subscribe model atPromiseThe use of source code preparation

paved

A Promise must be in one of three states:

  • PendingWait state: initial state, not success or failure state.
  • FulfilledCompleted state: indicates that the operation completed successfully.
  • RejectedFailed state: Indicates that the operation fails.
  • whenpromiseIn aPendingState, can be converted toFulfilledorRejected

    When the promise is Fulfilled or Rejected, the state cannot be changed again

So what triggers a state change in a promise? Let’s take a look at some chestnuts:

// p1 does nothing and passes an empty function
const p1 = new Promise(() = > {});
console.log("p1: ", p1);

// p2 Run resolve
const p2 = new Promise((resolve, reject) = > {
  resolve("success");
});
console.log("p2: ", p2);

// p3 reject
const p3 = new Promise((resolve, reject) = > {
  reject("fail");
});
console.log("p3: ", p3);

// p4 throws an error
const p4 = new Promise((resolve, reject) = > {
  throw Error("error");
});
console.log("p4: ", p4);

P5 Execute resolve and reject
const p5 = new Promise((resolve, reject) = > {
  resolve("success");
  reject("fail");
});
console.log("p5: ", p5);

// p6 executes nothing and takes no arguments
const p6 = new Promise(a);console.log("p6: ", p6);
Copy the code

Let’s take a look at the output:

From the output results, we can find:

  • createpromiseObject, a function is passed in (otherwise an error is reported, see p6) and executed immediately
  • promiseThe initial state of isPending(p1)
  • performresolve()reject()Can bepromiseThe status of theFulfilledRejected(see p2, p3)
  • ifpromiseThrows an exception, equivalent to executingreject(see p4)
  • promiseState transitions can only be made byPendingStart (see P5)

Based on our analysis of the output, let’s write the first version of promise code.

Implement the base Promise — first release

Promise constructor implementation

  1. First of all definepromiseThe three states of
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
Copy the code
  1. definePromiseConstructor to add the required properties

Promises/A+ : Promises/A+

  • valueIs arbitraryJavaScriptValid values (includingundefined)
  • reasonIs used to representpromiseWhy was it rejected

We use the ES6 class to define the Promise class, value/ Reason to be undefined, and status to be initially PENDING

class Promise {
  constructor() {
    this.value = undefined;
    this.reason = undefined;
    this.status = PENDING; }}Copy the code
  1. definepromiseYou need to pass in the functionexecutor
  • executorThere are two parameters, respectivelyResolve to reject, and both arguments are functions
  • executorWill be executed immediately
class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    this.status = PENDING;
    // Define resolve and reject
    const resolve = () = > {};
    const reject = () = > {};
    // The constructor executes immediatelyexecutor(resolve, reject); }}Copy the code
  1. implementationresolverejectThe function of the

When the promise state is Pedding: The resolve function can change the promise from Pending to depressing, and update the promise value. The Reject function converts a promise from Pending to Rejected and updates the promise’s Reason value

Note: The promise state can only be Fulfilled Pending -> Fulfilled and Pending -> Fulfilled

Therefore, when defining resolve and REJECT functions, the internal state of the promise needs to be determined first. If the state is pending, the value value and the promise state can be updated.

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    this.status = PENDING;

    const resolve = (value) = > {
      // Check whether the current state is Pending
      // Promise state transitions can only start with Pending
      if (this.status === PENDING) {
        // Update the value and promise state
        this.value = value;
        this.status = FULFILLED; }};const reject = (reason) = > {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED; }}; executor(resolve, reject); }}Copy the code

Source code here, the package will have doubts about the first question of this article, to, on the problem.

Question 1: Why do resolve/reject functions use the arrow function definition?

The answer to the question packet here first do not speak, we first think about thinking, to the end of the packet a answer.

  1. Promise A+The code says,PromiseWhen a thrown exception is executed, the failed function is executed. So we need to captureexecutorIf an exception exists, executerejectFunction.

class Promise {
    / /... The extra code is omitted for now
    Catch an Executor exception
    try {
      executor(resolve, reject);
    } catch (e) {
      Call reject when an exception occursreject(e); }}}Copy the code

Now that we’ve implemented the main part of the Promise, let’s move on to the other core then method for implementing promises.

Implement the basic functionality of the THEN method

The then method has a lot of considerations, so let’s read the specification together with some examples.

  1. promise.thenTwo parameters are accepted:
promise.then(onFulfilled, onRejected);
Copy the code

Define the then function that takes two arguments

class Promise {
  then (onFulfilled, onRejected) {}
}
Copy the code
  1. onFulfilledonRejectedIs an optional argument, or if both are not functionsignoreDrop (is it really simply ignore? See belowValue through)
  2. ifonFulfilledTheta is a function when thetapromiseThe status ofFulfilled, the callonFulfilledThe function,onRejectedSimilar, whenpromiseThe status ofRejetedWhen the call.

Let’s continue with a few chestnuts:

/ / resolve execution
const p1 = new Promise((resolve, reject) = > {
  resolve(1);
});
p1.then(
  (v) = > {
    console.log("onFulfilled: ", v);
  },
  (r) = > {
    console.log("onRejected: ", r); });/ / reject
const p2 = new Promise((resolve, reject) = > {
  reject(2);
});
p2.then(
  (v) = > {
    console.log("onFulfilled: ", v);
  },
  (r) = > {
    console.log("onRejected: ", r); });// Throw an exception
const p3 = new Promise((resolve, reject) = > {
  throw new Error("Promise execution error");
});
p3.then(
  (v) = > {
    console.log("onFulfilled: ", v);
  },
  (r) = > {
    console.log("onRejected: ", r); });Copy the code

Let’s take a look at the output:

With the output, we can discover the call logic for THEN

  • performresolveLater,promiseThe state changes toFulfilled.onFulfilledFunction call with an argument value ofvalue.
  • performrejectOr throw an error,promiseThe state changes toRejectedonRejectedFunction call with an argument value ofreason.

Next, let’s analyze the implementation idea of THEN.

The then function judges the current state of the promise. If it is a depressing state, the ondepressing function will be performed. In the Rejected state, execute the onRejected function. The idea is very simple, so let’s implement it.

class Promise {
  then(onFulfilled, onRejected) {
    // Call ondepressing when the state is depressing
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    // Call the onRejected function when the state is Rejected
    if (this.status === REJECTED) {
      onRejected(this.reason); }}}Copy the code

Question 2: Does a promise state appear Pending when the THEN method executes

A promise registers multiple THEN methods

We continue to read the specification:

If a PROMISE calls THEN multiple times: When the promise state is a big promise, all ondepressing functions will be called in the registration order. When the Promise state is Rejected, all the onRejected functions are called in the registration order.

What does this specification mean? Small bag to give a chestnut:

const p = new Promise((resolve, reject) = > {
  resolve("success");
});

p.then((v) = > {
  console.log(v);
});
p.then((v) = > {
  console.log(`${v}- 111 `);
});
Copy the code

Output result:

success;
success---111;
Copy the code

Using the example above, the specification can be put simply: multiple THEN methods can be registered for the same promise, and when a promise completes or fails, the corresponding THEN methods are executed in the order they were registered.

This specification is already compatible with our code. With that in mind, let’s put together the first version of Promise and test the code we’ve written so far.

// Promise has three states
// State can only be PENDING -> pity /REJECTED
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    // The initial state is Pending
    this.status = PENDING;
    // This points to the problem
    const resolve = (value) = > {
      // Check whether the current state is Pending
      // Promise state transitions can only start with Pending
      if (this.status === PENDING) {
        // Update the value and promise state
        this.value = value;
        this.status = FULFILLED; }};const reject = (reason) = > {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED; }};try {
      Catch an Executor exception
      executor(resolve, reject);
    } catch (e) {
      Call reject when an exception occursreject(e); }}then(onFulfilled, onRejected) {
    // Call ondepressing when the state is depressing
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    // Call the onRejected function when the state is Rejected
    if (this.status === REJECTED) {
      onRejected(this.reason); }}}Copy the code

To test the basic case, the output is as follows:

Then test registering multiple THEN methods for the same Promise, and the output is

success;
success---111;
Copy the code

The first version of the code can meet the current specification, ~~~, relax, we continue to implement.

Handling asynchronous functionality — second edition

As we mentioned at the beginning of this article, promise is a solution for asynchronous programming, so let’s test if the first version of Promise can implement asynchrony.

const p = new Promise((resolve, reject) = > {
  Use setTimeout to simulate asynchrony
  setTimeout(() = > {
    resolve("success");
  });
});

p.then((v) = > {
  console.log(v);
});
p.then((v) = > {
  console.log(`${v}- 111 `);
});
Copy the code

There is no output, so the first version of the code so far is not asynchronous programming, let’s look at why.

If there is an asynchronous call inside the Promise, when the then function is executed, the state of the Promise is still Pending because resolve/ Reject is blocked in the asynchronous callback. OnFulfilled and onRejected in the first version of the THEN callback cannot be executed.

Publish and subscribe model

In order to better realize the native promise writing, here we plug in a little knowledge.

There is an idea that is often used in asynchronous programming called the publish-subscribe model. Publish subscription mode is based on an event (topic) channel. The object Subscriber that wants to receive notification subscribes to the topic through user-defined events, and the object Publisher that is activated notifies each Subscriber that subscribes to the topic by publishing the topic event.

There are three roles in the publish and subscribe mode, Publisher, Event Channel and Subscriber.

It is a little hard to understand just by definition, for example: Take the current hit drama the world as an example. The world is so hot that I can’t settle down at work. Every day, I can’t wait for the world to update, and I want to start watching the drama at the first moment when the world updates. You can’t refresh your page every second to see if the world is up to date. The platform is humanized, it provides the message subscription function, if you choose to subscribe, the platform will update the world, the first time to send messages to inform you, after subscription, you can happily follow the drama.

Above, we are the Subscriber who follow the drama, the human drama is the Publisher, and the platform is the event channel. When the human drama is released, the platform will notify all the subscribers.

For more details, see the package’s blog post: Observer vs. Publish and subscribe. Don’t get confused

So how do we design asynchronous Promise? Let’s break down the Promise functionality by publishing and subscribing:

  • thenThe callbackonFulfilled/onRejectedfunction
  • resolve/rejectfunction
  • resolve/rejectAfter the function is executed,promiseState change,thenThe callback function executes

Only after the resolve/ Reject function is executed, the corresponding ondepressing /onRejected function can be executed. However, due to asynchronous invocation, the resolve/ Reject function is executed later than the THEN function. Therefore, ondepressing /onRejected can be interpreted as the subscriber and subscribe to the resolve/ Reject function execution. Resolve /reject is the publisher; Promise provides the function of an event channel and stores the onFulfilled/onRejected subscription. Because multiple THEN callbacks can be registered for the same Promise object, the Event Channel stores the callbacks in an array format

Therefore, we need to modify the implementation of resolve/ Reject to notify the corresponding subscriber to execute when both are called.

Asynchronous implementation

  1. inPromiseDefines two arrays inonFulfilledCallbacksonRejectedCallbacks, respectively for storagethenThe callbackonFulfilledonRejectedfunction
class Promise {
  // Store the subscription onFulfilled and onRejected functions
  this.onFulfilledCallbacks = [];
  this.onRejectedCallbacks = [];
}
Copy the code
  1. thenMethod is executed ifPromiseIn aPendingState,onFulfilledonRejectedThe function subscribes toonFulfilledCallbacksonRejectedCallbacks– waiting forresolve/rejectExecution (event publishing)
then(onFulfilled, onRejected) {
    if (this.status === PENDING) {
        // When a promise is pending, the callback subscribes
        this.onFulfilledCallbacks.push(onFulfilled);
        this.onRejectedCallbacks.push(onRejected); }}Copy the code
  1. callresolve/reject, release the event, respectively execute correspondingonFulfilledCallbacksonRejectedCallbacksFunctions in arrays
// Perform publishing
const resolve = (value) = > {
  if (this.status === PENDING) {
    this.value = value;
    this.status = FULFILLED;
    // Perform the ondepressing function successively
    this.onFulfilledCallbacks.forEach((cb) = > cb(this.value)); }};const reject = (reason) = > {
  if (this.status === PENDING) {
    this.reason = reason;
    this.status = REJECTED;
    // Execute the onRejected function in turn
    this.onRejectedCallbacks.forEach((cb) = > cb(this.reason)); }};Copy the code

We put the above code together to form the second version of the code and test the case.

// Asynchronous invocation
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    this.status = PENDING;
    // Store the subscription onFulfilled and onRejected functions
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) = > {
      if (this.status === PENDING) {
        this.value = value;
        this.status = FULFILLED;
        // When the resolve function is called, notify the subscriber of ondepressing execution
        this.onFulfilledCallbacks.forEach((cb) = > cb(this.value)); }};const reject = (reason) = > {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
        // Notify the subscriber of onRejected when the reject function is called
        this.onRejectedCallbacks.forEach((cb) = > cb(this.reason)); }};try {
      executor(resolve, reject);
    } catch (e) {
      console.log(e); reject(e); }}then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
    if (this.status === PENDING) {
      // When a promise is pending, the callback subscribes
      this.onFulfilledCallbacks.push(onFulfilled);
      this.onRejectedCallbacks.push(onRejected); }}}Copy the code

Using the previous example to test, the output is

success
success--111
Copy the code

The above example is a bit simple, let’s test a more complex one:

console.log(1);
setTimeout(() = > {
  console.log(2);
})
const p1 = new Promise((resolve) = > {
  console.log(3);
  setTimeout(() = > {
    resolve(4);
  })
})
p1.then(v= > console.log(v));
console.log(5);
Copy the code

Browser output:

Code output of the second edition:

The browser and version 2 output the same result, so you can see that asynchronous Promise is now available in version 2.

But is it really okay? Let’s tweak the case slightly to remove the asynchronous call from the Promise and see if the browser output is the same as the second version.

console.log(1);
setTimeout(() = > {
  console.log(2);
})
const p1 = new Promise((resolve) = > {
  console.log(3);
  resolve(4);
})
p1.then(v= > console.log(v));
console.log(5);
Copy the code

Browser output:

Code output of the second edition:

We can obviously see that the second version of the code is the opposite of the browser’s 2 and 4 output, right? Then then setTimeout?

Question 3: Why does the browser execute the then callback first and then setTimeout?

Chain call – 3rd edition

With asynchrony completed, we continue to implement chain calls to the then method. First we continue to read the specification:

  1. thenMethod must return onepromise
promise2 = promise1.then(onFulfilled, onRejected)
Copy the code

Promise2 is the return value of the then function, also a Promise object.

then(onFulfilled, onRejected) {
  / /... Redundant code omission
  cosnt promise2 = new Promise((resolve, reject) = > {})
  return promise2;
}
Copy the code
  1. ifonFulfilledonRejectedThe return value isx, then runPromise Resolution Procedure [[Resolve]](promise2, x)(Resolve (x) of promise2)

Let’s take a look at this specification:

// resolve
console.log(new Promise((resolve) = > {
    resolve(1)
}).then((x) = > x))
// reject
console.log(new Promise((resolve, reject) = > {
    reject(1)
}).then(undefined.(r) = > r))
Copy the code

Promise returns the same result as resolve and reject.

Let’s read the specification one more time:

  • ifonFulfilledonRejectedThe return value isxBoth of the above functions(v) => v, the passed parameter values are1, therefore returns a valuex = 1;
  • executepromise2resolve(x)Delta function, and thenthenreturnpromise2Object – so both of the above functions are callspromise2resolveFunction, so both return values are infulfilledThe state of thepromiseObject and all values are1.

Since we need to use the return value of the onFulfilled/onRejected function as the parameter value of promise2 Resolve, we need to move the then function into promise2 as a whole.

then (onFulfilled, onRejected) {
  let promise2 = new Promise((resolve, reject) = > {
      if (this.status === FULFILLED) {
        // Return the value as the resolve argument value
        let x = onFulfilled(this.value);
        resolve(x);
      }
      if (this.status === REJECTED) {
        let x = onRejected(this.reason); resolve(x); }});return promise2;
}
Copy the code

Do you think this will implement the specification? NONONO!!!

Difficult points: Synchronous code does work, but imagine a scenario where asynchronous code exists in a Promise, asynchronous logic is designed to execute then, and if the Promise is in a Pending state, OnFulfilled/onRejected function first subscribe to onFulfilledCallbacks/onRejectedCallbacks, means that the two functions do not perform in the then, so we should be how to obtain both the return value of that?

So we can’t just use this. OnFulfilledCallbacks. Push press the callback function (onFulfilled) into the event channel storage array, we make a layer of packaging to the callback function, Encapsulate the resolve function of promise2 with onFulfilled, so that when the onFulfilled is performed, the return value x can be obtained to return the promise2 state, as shown in the following code:

// Use the anonymous arrow function to ensure that the internal this points to() = > {// The callback function executes and gets its return value
  let x = onFulfilled(this.value);
  // Execute the resolve method of promise2
  resolve(x);
}
Copy the code

So the Pending state code looks like this:

if (this.status === PENDING) {
  // Use the anonymous function to bind resovle with ondepressing
  this.onFulfilledCallbacks.push(() = > {
    let x = onFulfilled(this.value);
    resolve(x);
  });
  this.onRejectedCallbacks.push(() = > {
    let x = onRejected(this.reason);
    resolve(x);
  });
}
Copy the code
  1. ifonFulfilledonRejectedAn exception was thrown during executioneThe callpromise2reject(e)To return topromise2

Let’s test it with chestnuts:

console.log(new Promise((resolve) = > {
    resolve(1)
}).then(() = > {
    throw new Error('resolve err')}))console.log(new Promise((resolve, reject) = > {
    reject(1)
}).then(undefined.() = > {
    throw new Error('reject err')}))Copy the code

According to the output result, when onFulfilled/onRejected fails, promise2 will execute its reject function. So we need to add a layer of exception catching to our current code and change it to look like this:

then(onFulfilled, onRejected) {
  let p1 = new Promise((resolve, reject) = > {
    if (this.status === FULFILLED) {
      // Add exception capture
      try {
        // Return the value as the resolve argument value
        let x = onFulfilled(this.value);
        resolve(x);
      } catch(e) { reject(e); }}/ /... The rest is similar
  return promise2;
}
Copy the code
  1. ifonFulfilledIt’s not a function andpromiseThe status ofFulfilled, thenpromise2The same value should be accepted and the state isFulfilled

What does this specification mean? Let’s take a chestnut:

// Output result 1
const p1 = new Promise((resolve) = > {
    resolve(1)
})
p1.then(x= > x).then().then().then().then().then(x= > console.log(x))
Copy the code

The final output result of the above program is 1, and the value of the first resolve is 1. It can be seen that when ondepressing is not a function, the promise value will be transferred along the then, until ondepressing becomes a function.

This is also the value transfer of the Promise. When the ondepressing of then becomes a non-function, the value will be passed on until the function ondepressing is met

  1. ifonRejectedIt’s not a function andpromiseThe status ofRejected, thenpromise2The same reason should be accepted and the state isRejected
Error: Error at 
      
       :4:33
      
const p1 = new Promise((resolve) = > {
    reject(1)
})
p1.then(undefined.() = > {throw Error('error')}).then().then().then().then().then(x= > console.log(x), (r) = > console.log(r))
Copy the code

Similar to onFulfilled, the Promise also provides compatibility with the onRejected function, and error transmission will occur.

Through the cases in Article 4 and 5, we can find that when onFulfilled/onRejected is a non-function type, the Promise will pass value and exception respectively.

How can we pass values or exceptions continuously? (See code below)

  • Value passing: Value passing is very simple. We just need to define a function that takes x and returns x
  • Exception: Defines the function parameter value as an exception and then throws this exception continuously.
x => x;
e= > throw e;
Copy the code
then(onFulfilled, onRejected) {
  // Check whether the argument is a function. If not, use the default function instead
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) = > v;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : (e) = > {
          throw e;
        };
  let promise2 = new Promise((resolve, reject) = >{});return promise2;
}
Copy the code

At this point, the chain-call part is done for now, so let’s integrate version 3 of the Promise code.

const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    this.status = PENDING;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    const resolve = (value) = > {
      if (this.status === PENDING) {
        this.value = value;
        this.status = FULFILLED;
        this.onFulfilledCallbacks.forEach((cb) = > cb(this.value)); }};const reject = (reason) = > {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
        this.onRejectedCallbacks.forEach((cb) = > cb(this.reason)); }};try {
      executor(resolve, reject);
    } catch(e) { reject(e); }}then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) = > v;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (e) = > {
            throw e;
          };
    let promise2 = new Promise((resolve, reject) = > {
      if (this.status === FULFILLED) {
        // Add exception capture
        try {
          // Return the value as the resolve argument value
          let x = onFulfilled(this.value);
          resolve(x);
        } catch(e) { reject(e); }}if (this.status === REJECTED) {
        try {
          let x = onRejected(this.reason);
          resolve(x);
        } catch(e) { reject(e); }}if (this.status === PENDING) {
        // Use the anonymous function to bind resovle with ondepressing
        this.onFulfilledCallbacks.push(() = > {
          try {
            let x = onFulfilled(this.value);
            resolve(x);
          } catch(e) { reject(e); }});this.onRejectedCallbacks.push(() = > {
          try {
            let x = onRejected(this.reason);
            resolve(x);
          } catch(e) { reject(e); }}); }});returnpromise2; }}Copy the code

Let’s test if we can implement the chain call:

// The output is 4, indicating that the chained call to resolve is feasible and value passing is implemented
const p1 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve(1);
  });
});
p1.then((v) = > v + 1)
  .then((v) = > v * 2)
  .then()
  .then((v) = > console.log(v));

// Output Error1, indicating that the chain call is still successful.
const p2 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject(1);
  });
});
p2.then(
  () = > {},
  (r) = > new Error(r)
).then(
  (v) = > console.log("v", v),
  (r) = > console.log("r", r)
);
Copy the code

At this point, the third version of the code has been successfully implemented, followed by the core resolvePromise section, which is a bit more complex, so the package has decided to write an article about it in detail.

I want to answer

Why do resolve/reject functions use the arrow function definition?

In a word: This refers to the problem.

Let’s change it to the normal function form:

class Promise {
  constructor(executor) {
    this.value = undefined;
    this.reason = undefined;
    const resovle = function (value) {
      console.log(this);
      this.value = value;
    }
    const reject = (reason) = > {
      console.log(this);
      this.reason = reason;
    }
    executor(resovle, reject)
  }
}

Copy the code

Then we execute the following code:

var value = 1;
new Promise((resolve, reject) = > {
  resolve(100)})Copy the code

The output of this is undefined. Because resolve is a normal function, the call in Promise is the default, with this non-strictly pointing to window and this strictly pointing to undefined. ES6 class defaults to strict mode and therefore points to undefined. So using normal functions, we don’t get the value property in the Promise.

Promise {value: undefined, reason: 200}
var reason = 2;
new Promise((resolve, reject) = > {
  reject(200)})Copy the code

Reject uses the arrow function, which has no this of its own, so it uses this of the outer scope along the scope chain. So we can get the Reason attribute.

If you want to know more about this, you can refer to my blog post “38 Interview Questions in 2W Characters” to completely clarify the questions this points to in JS

Does the PROMISE state appear pending when the then method executes

Will appear, as mentioned in the article, when there is asynchronous code in a Promise, for example

new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve(1)})})Copy the code

Why do browsers perform then callbacks and then setTimeout?

This is caused by the Event Loop mechanism of JavaScript. Then callback is microtask and setTimeout is macro task. After the execution of synchronous code is completed, the main program will first search for tasks in the microtask queue, and the macro task queue will be executed only after all the microtask queue is completed.

If you want to learn more about Event loops, you can refer to the package’s blog post understanding EventLoop from top to bottom in JavaScript

Code warehouse

The JS and ES6 blog posts and articles that follow the package are stored in the current repository.

Source code address: Promise foundation

If you feel helpful, don’t forget to give the small bag a ⭐.

After the language

I am battlefield small bag, a fast growing small front end, I hope to progress together with you.

If you like xiaobao, you can pay attention to me in nuggets, and you can also pay attention to my small public number – Xiaobao learning front end.

All the way to the future!!

An early end to the epidemic will restore peace to the world