Interviewer: Write a Promise, resolve()/reject()/then()…

A directory

What’s the difference between a free front end and a salted fish

directory
A directory
The second myth
Three simple handwritten promises
Four written Promise
Five Promise. All ()
Six Promise. Race ()
Promise asynchronous scheduler
Viii References

The second myth

Returns the directory

The evolution of asynchronous schemes in JavaScript:

  • callback -> promise -> generator -> async/await

The myth of naive reductionism prevails in the computer industry: the closer you get to the bottom, the more skilled you get.

Every programmer has a desire to read the underlying source code.

This is partly true.

However, we should also see that once there is a domain gap between the bottom layer and the surface layer.

Mastery of the bottom level does not mean mastery of the surface level.

For example, the developer of the game is not necessarily the best in the game. This is especially true in FPS shooters or fighting games, where the vast majority of the best players can’t write code at all.

If you define mastery of promises as being good at using promises to solve problems in a variety of asynchronous scenarios.

So, being good at writing Promise implementations doesn’t do much for mastering promises.

This text is from industrial poly

Three simple handwritten promises

Returns the directory

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';

function myPromise(fn) {
  const that = this;
  that.status = PENDING;
  that.value = null;
  that.reason = null;

  that.resolvedCallbacks = [];
  that.rejectedCallbacks = [];

  function resolve(value) {
    if (that.status === PENDING) {
      that.status = RESOLVED;
      that.value = value;
      that.resolvedCallbacks.map(cb= >cb(value)); }}function reject(reason) {
    if (that.status === PENDING) {
      that.status = REJECTED;
      that.reason = reason;
      that.rejectedCallbacks.map(cb= >cb(reason)); }}try {
    fn(resolve, reject);
  } catch(e) {
    reject(e);
  }
}

myPromise.prototype.then = function(onFullfilled, onRejected) {
  const that = this;
  
  if (that.status === PENDING) {
    that.resolvedCallbacks.push(onFullfilled);
    that.rejectedCallbacks.push(onRejected);
  }

  if (that.status === RESOLVED) {
    onFullfilled(that.value);
  }

  if (that.status === REJECTED) {
    onFullfilled(that.reason);
  }

  return that;
}

const p = new myPromise((resolve, reject) = > {
  setTimeout(() = > {
    resolve(1000);
  }, 1000);
});

p.then((res) = > {
  console.log('Result:', res); // Result: 1000
}).then(() = > {
  console.log('jsliang'); // jsliang
})
Copy the code

This code reads like this on Jsliang’s personal understanding:

  1. There arepending,resolvedAs well asrejectedThese three states. See the first three lines of this code.
  2. Once the state ispendingresolveorrejectedChange, then you cannot change again. See the codefunction resolvefunction reject.
  3. Code execution to.thenIs divided into two cases: one is to go asynchronous, the state becomesPENDING, take the first logic; The second isRESOLVEDorREJECTED“, takes the second and third logic.
  4. In the case of the first logic, because we’re in an asynchronous scenario, we’re innSeconds later gofunction resolveorfunction rejectAnd in the two method bodies,that.resolvedCallbacks.map(cb => cb(value))orthat.rejectedCallbacks.map(cb => cb(reason))Will execute the callback method we saved to implementPromise.then()The function.

If the partner is still not clear, you can type the code twice more. If the partner is not interested in jsliang’s understanding, you can look at the Promise A+ specification in the reference, which is more conducive to the understanding of the partner.

Four written Promise

Returns the directory

Code source industry poly that article:

At first, I thought the code was sound, but I was too embarrassed to let Jsliang write it silently, so I used the code from the previous chapter.

/ * * *@name JsliangPromise
 * @description Handwritten Promise * /

/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --, set the share function -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
// check whether it is a function
const isFunction = (obj) = > {
  return typeof obj === 'function';
};

// Determine whether it is an object
const isObject = (obj) = > {
  return!!!!! (obj &&typeof obj === 'object');
};

// Check whether it is thenable
const isThenable = (obj) = > {
  return (
    isFunction(obj)
    || isObject(obj)
  ) && 'then' in obj;
};

// Check whether this is a Promise
const isPromise = (promise) = > {
  return promise instanceof JsliangPromise;
};


/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --, set the state of Promise -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
/** 1. Promise has 3 states: pending, fulfilled, fulfilled * pending: Promise can switch to fulfilled or fulfilled * fulfilled: There must be an immutable value */ in the rejected state and there must be an immutable reason */
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - Promise supplement process -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /

// 3.6 handleCallback () According to the state, judge whether this path is a pity path or rejected path
const handleCallback = (callback, state, result) = > {
  / / 3.6.1 deconstruction
  const { onFulfilled, onRejected, resolve, reject } = callback;

  / / 3.6.2 judgment
  try {
    // 3.6.3 If successful
    if (state === FULFILLED) {
      3.6.4 Judge whether ondepressing is a function
      if (isFunction(onFulfilled)) {
        // 3.6.5 If so, make its return value the result of the next Promise
        resolve(onFulfilled(result));
      } else {
        3.6.6 If not, use the result of the current Promise as the result of the next Promiseresolve(result); }}else if (state === REJECTED) {
      if (isFunction(onRejected)) {
        resolve(onRejected(result));
      } else{ reject(result); }}}catch (error) {
    // 3.6.7 This error is used as the rejected Reason of the next Promisereject(error); }};2.3.4 Clear the previous content
const handleCallbacks = (callbacks, state, result) = > {
  while(callbacks.length) { handleCallback(callbacks.shift(), state, result); }};// 2.3 Once the state is not pending, it cannot be converted again
const transition = (promise, state, result) = > {
  // 2.3.1 If pending, the corresponding state and result should be set
  if(promise.state ! == PENDING) {return;
  }

  // 2.3.2 If not, set it
  promise.state = state;
  promise.result = result;

  2.3.3 Asynchronously clear all callbacks when the status changes
  setTimeout(() = > {
    handleCallbacks(promise.callbacks, state, result);
  }, 0);
}

2.6 If some special values are resolved, special operations must be performed.
// This special treatment is also explicitly described in the specification
const resolvePromise = (promise, result, resolve, reject) = > {
  // 2.6.1 If result is the current Promise itself, TypeError is raised
  if (result === promise) {
    return reject(new TypeError('Can not fulfill promise with itself'));
  }

  2.6.2 If result is another Promise, the current state and result state are used
  if (isPromise(result)) {
    return result.then(resolve, reject);
  }

  // 2.6.3 If result is a thenable object
  // Call The then function to re-enter The Promise Resolution procedure
  if (isThenable(result)) {
    try {
      if (isFunction(result.then)) {
        return newJsliangPromise(then.bind(result)).then(resolve, reject); }}catch (error) {
      returnreject(error); }}// 2.6.4 If no, directly resolve result
  resolve(result);
};

/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --, Promise to realize -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /

// 2. Set Promise
const JsliangPromise = function(f) {
  // 2.1 Setting the initialization status
  this.state = PENDING;
  this.result = null;

  // 3.1.then () can be called multiple times, so you need to set the array to record
  this.callbacks = [];

  // this is a big pity; // this is a big pity; // this is a big pity
  const onFulfilled = value= > transition(this, FULFILLED, value);
  const onRejected = reason= > transition(this, REJECTED, reason);

  // 2.3 Use ignore to ensure that resolve/reject is called only once
  let ignore = false;
  
  // 2.4 Set the resolve processing mode
  let resolve = (value) = > {
    if (ignore) {
      return;
    }
    ignore = true;
    // 2.5 Determine three rules for resolve
    resolvePromise(this, value, onFulfilled, onRejected);
  };

  let reject = (reason) = > {
    if (ignore) {
      return;
    }
    ignore = true;
    onRejected(reason);
  }

  // 2.6 Try
  try {
    // 2.6.1 Pass resolve and reject as arguments to f for easy call
    f(resolve, reject);
  } catch (error) {
    If f fails, reject (reject) is used as a reasonreject(error); }}// 3. Promise.then
JsliangPromise.prototype.then = function (onFulfilled, onRejected) {
  The 3.2. then() method returns a Promise, so you need to return one
  return new JsliangPromise((resolve, reject) = > {

    // 3.3 Set callback
    const callback = { onFulfilled, onRejected, resolve, reject };

    3.4 If state is pending, store it in the Callbacks list
    if (this.state === PENDING) {
      this.callbacks.push(callback);
    } else {
      If not, throw a handleCallback
      // Why use setTimeout? Since we can't simulate microtasks, let's do macro tasks instead
      setTimeout(() = > {
        handleCallback(callback, this.state, this.result);
      }, 0); }}); };/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- test -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /

const promise = new JsliangPromise((resolve, reject) = > {
  setTimeout(() = > {
    console.log(1);
    resolve(2);
  }, 1000);
});

promise.then((res) = > {
  console.log('res 1:, res);
  return 3;
}).then((res) = > {
  console.log('res 2:, res);
});
Copy the code

Five Promise. All ()

Returns the directory

Now let’s practice some questions.

Given content:

const a = new Promise((resolve) = > {
  setTimeout(() = > {
    resolve(500);
  }, 500);
});
const b = new Promise((resolve) = > {
  setTimeout(() = > {
    resolve(1000);
  }, 1000);
});

Promise.myAll([a, b]).then((res) = > {
  console.log('Result:', res);
});
Copy the code

Required output: [500, 1000].

Promise.myAll = function(arr) {
  // 1. Return a Promise
  return new Promise((resolve, reject) = > {

    // 2. Set the final return result
    const result = [];

    // 3. Get the length of the array and the current progress index index
    const length = arr.length;
    let index = 0;

    // 4. Go through the list
    for (let i = 0; i < arr.length; i++) {

      // 5 set the content for result in. Then
      // If index ends, resolve(result)
      / / otherwise reject (err)
      arr[i].then((res) = > {
        result[i] = res;
        
        index++;

        if (index === length) {
          resolve(result);
        }
      }).catch((err) = > {
        throw new Error(err); })}})};Copy the code

Six Promise. Race ()

Returns the directory

Given content:

const a = new Promise((resolve) = > {
  setTimeout(() = > {
    resolve(500);
  }, 500);
});
const b = new Promise((resolve) = > {
  setTimeout(() = > {
    resolve(1000);
  }, 1000);
});

Promise.myRace([a, b]).then((res) = > {
  console.log('Result:', res);
});
Copy the code

Required output: 500.

The race method is similar to the previous all method, except that it returns the result the first time it executes.then, rather than every result.

Promise.myRace = function(arr) {
  return new Promise((resolve, reject) = > {
    for (let i = 0; i < arr.length; i++) {
      arr[i].then((res) = > {
        return resolve(res);
      }).catch((err) = > {
        throw new Error(err); })}})};const a = new Promise((resolve) = > {
  setTimeout(() = > {
    resolve(500);
  }, 500);
});
const b = new Promise((resolve) = > {
  setTimeout(() = > {
    resolve(1000);
  }, 1000);
});

Promise.myRace([a, b]).then((res) = > {
  console.log('Result:', res);
})
Copy the code

Promise asynchronous scheduler

Returns the directory

Review the problem and complete the following code:

** The Scheduler class in the following code is improved so that the program can output */ correctly

class Scheduler {
  add(promiseCreator) {
    // ...
  }
  // ...
}

const timeout = (time) = > {
  return new Promise((resolve) = > {
    setTimeout(() = > {
      resolve();
    }, time);
  });
};
const scheduler = new Scheduler();
const addTack = (time, order) = > {
  return scheduler
    .add(() = > timeout(time))
    .then(() = > console.log(order));
};
addTack(1000.'1');
addTack(500.'2');
addTack(300.'3');
addTack(400.'4');

// Output: 2 3 1 4
// At the beginning, tasks 1 and 2 are queued
// At 500ms, complete 2, output 2, task 3 enter the queue
// At 800ms, complete 3, output 3, task 4 enter the queue
// At 1000ms, complete 1, output 1, no next queue
// at 1200ms, complete 4, output 4, no next queue
// Queue completion, output 2 3 1 4
Copy the code

Implementation (async/await) :

** The Scheduler class in the following code is improved so that the program can output */ correctly

class Scheduler {
  constructor(maxNum) {
    this.taskList = [];
    this.count = 0;
    this.maxNum = maxNum; // Maximum number of concurrent requests
  }
  async add(promiseCreator) {
    // If the current concurrency exceeds the maximum concurrency, the task queue is entered
    if (this.count >= this.maxNum) {
      await new Promise((resolve) = > {
        this.taskList.push(resolve); })}// The number of times + 1 (if the previous execution is not complete, keep adding)
    this.count++;

    // Wait for the contents to complete
    const result = await promiseCreator();

    // Number of times -1
    this.count--;

    // The first team to leave the team
    if (this.taskList.length) {
      this.taskList.shift()();
    }

    // return the result value
    returnresult; }}const timeout = (time) = > {
  return new Promise((resolve) = > {
    setTimeout(() = > {
      resolve();
    }, time);
  });
};

const scheduler = new Scheduler(2);
const addTack = (time, order) = > {
  return scheduler
    .add(() = > timeout(time))
    .then(() = > console.log(order));
};
addTack(1000.'1');
addTack(500.'2');
addTack(300.'3');
addTack(400.'4');

// Output: 2 3 1 4
// At the beginning, tasks 1 and 2 are queued
// At 500ms, complete 2, output 2, task 3 enter the queue
// At 800ms, complete 3, output 3, task 4 enter the queue
// At 1000ms, complete 1, output 1, no next queue
// at 1200ms, complete 4, output 4, no next queue
// Queue completion, output 2 3 1 4
Copy the code

Viii References

Returns the directory

  • Promises/A+ 100 lines of code[Recommended Reading: 30min]
  • Implement Promises with minimal support for asynchronous chained calls (20 lines)【 Recommended reading: 20min】
  • Classic INTERVIEW Questions: The most detailed handwritten Promise tutorial ever[Recommended Reading: 30min]
  • 1. Write A Promise that Promises/A+ Promises from scratch[Reading advice: Probably read it all over, no clear analysis of the previous]
  • Promise implementation principle (source code attached)[Reading advice: Probably read it all over, no clear analysis of the previous]
  • Analyze the internal structure of Promise, step by step to achieve a complete Promise class that can pass all Test cases[Recommended reading: write in detail, not clear analysis of the previous]
  • Xiao Shao teach you to play promise source code[Recommended reading: write in detail, not clear analysis of the previous]
  • Promise won’t…? Look here!! The most accessible Promise ever!![Recommended reading: write in detail, not clear analysis of the previous]

Jsliang’s document library is licensed by Junrong Liang under the Creative Commons Attribution – Non-commercial – Share alike 4.0 International License. Based on the github.com/LiangJunron… On the creation of works. Outside of this license agreement authorized access can be from creativecommons.org/licenses/by… Obtained.