1. The introduction

Through handwriting in line with A+ standard promise, to in-depth understanding of the promise, combined with the relevant interview questions, strive to achieve in the interview, if asked promise, we can comprehensively beat the interviewer 😁😁😁 each of the following writing method corresponds to some of the characteristics of promise, constantly upgrading, It’s pretty easy to do it once you know how it works

2. A minimalist promise

2.1 Basic Features

Detailed introduction of words we go to see Ruan Yifeng ES6-Promise, I here when you have a certain basis, and then we summarize the basic characteristics

New Promise((resolve,reject)=>{//excutor setTiemout(()=>{resolve(1) //resolve will be passed to the successful callback argument},1000) }).then((val)=>{ //onFulfiled console.log(val) },(e)=>{ //onRejected console.log(e) })Copy the code
  1. The initial state value of the Promise object ispending
  2. Filfiled (success) and Rejected (failure) can be used to change the promise state in excutor.
  3. Once the state is changed the state is frozen and cannot be changed
  4. The callback function in the then method executes after the state changes, calling the successful callback and calling the failed callback
  5. The value in resolve is passed to the successful callback function argument (similar to failure)

2.2 implementation

Function points 1, 2, 3 and 5 above are easy to implement. 4 can also be implemented by publishing and subscription mode

Class Promise {constructor(executor) {this.status='pending' // this. Value = undefined // This. reason = undefined This. OnRejected = [] let resolve = (value)=>{if(this. Status === ='pending'){this Ondepressing. ForEach (fn=>fn(this.value)) // this will be a big pity. }} let reject = (reason)=>{if(this.status==='pending'){this.status= 'rejected' this.reason = reason This.onRejected. ForEach (fn=>fn(this.reason))}} Try {execute (resolve,reject)} Catch (e) {reject(e)}}  then(onFulfilled, OnFulfilled (this. Status ===' depressing '){onFulfilled(this. Value)} {onFulfilled(this. if(this.status==='rejected'){ onRejected(this.reason) } if(this.status==='pending'){ this.onFulfilled.push(onFulfilled) This.onrejected. Push (onRejected)}}Copy the code

3. Add chain calls

3.1 Chain characteristics

1. If the then method in a promise, whether it succeeds or fails, returns a normal callback to the success of the next THEN in the outer THEN

Promise.reject().then((val)=>{ return 'ok' },()=>{ return 'err' }).then((val)=>{ console.log('ok' + val) },(e)=>{ Console. log('err' + e)}) // Okerr returns a normal value for a failed callback to the first THEN, or a successful callback to the second THENCopy the code

2. If the return value of a successful or failed callback returns a PROMISE, the promise implementation is made to take its state

Promise.resolve().then(()=>{ return new Promise((resolve)=>{ setTimeout(()=>{ resolve(1) },1000) }) }).then((val)=>{ Console. log(val)}) // Prints 1 after one secondCopy the code

3.2 implementation

If a new Promise is returned after the first “then” call, then a new Promise is returned after the first “then” call. Just call the resolve method of the new promise(promise2) returned by THEN and the second tricky thing is what is the argument? If the return value is normal, if the return value is Piomise, so we package a resolvePromise method to handle the first THEN callback. The newly created promise2 and resolve. Reject in promisE2

Let resolvePromise = (promise2, x, resolve, Reject) => {... } class Promise { construcotr(){... } then(){let promise2 = new promise((resolve,reject)=>{let x = onFulfiled() resolvePromise(promise2,x, resolve, reject) }) return promiese2 } }Copy the code

The resolvePromise method will determine the return value type of onFulfiled. What if it is A common value, what if it is A Promise, and what if an error is reported

let resolvePromise = (promise2, x, resolve, Reject) => {// Monitor ring chain if(promise2===x) return new TypeError('chaining cycle detected for promise') if(typeof X ==='function' ||(typeof x ==='object' && x! If (typeof then === 'function'){if(typeof then === 'function'){if(typeof then === 'function'){ Call (x,resolve,reject)}else{// call(x,resolve,reject); // Call (x,resolve,reject); // } // resolve(x)} // catch (e) {reject(e)} // resolve(x)} // class Promise {constructor(executor) { this.status = 'pending' this.value = undefined this.reason = undefined this.onFulfilledCallback = [] this.onRejectedCallback = [] let resolve = (value) => { if (this.status === 'pending') { this.status = 'fulfilled' this.value = value this.onFulfilledCallback.forEach(fn => fn(this.value)) } } let reject = (reason) => { if (this.status  === 'pending') { this.status = 'rejected' this.reason = reason this.onRejectedCallback.forEach(fn => fn(this.reason)) }  } try { executor(resolve, reject) } catch (e) { reject(e) } } then(onFulfilled, Onpromise2 = new promise1 ((resolve, resolve)) {// Let promise2 = new promise1 ((resolve, resolve)); (this. Status === 'depressing ') {setTimeout(() => {// This is a pity. This is a big pity (this. Value) resolvePromise(promise, x, resolve, reject) } catch (e) { reject(e) } }) } if (this.status === 'rejected') { setTimeout(() => { try { let x = onRejected(this.reason) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) } if (this.status === 'pending') { this.onFulfilledCallback.push(() => { setTimeout(() => { try { let x = onFulfilled(this.value) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) }) this.onRejectedCallback.push(() => { setTimeout(() => { try { let x = onRejected(this.reason) resolvePromise(promise2, x, resolve, reject) } catch (e) { reject(e) } }) }) } }) return promise2 } }Copy the code

Basic interview 5-10 minutes of code written here, can give full marks, the rest is 4 patches

4. The patch

Patch 4.1 points

In fact, it is A+ standard test case patch, I order the importance of the first must be able to write (interview can not write), the back can know

  1. Default parameter configuration for THEN
  2. X could be a Promise, its return value could be a Pormise, and the Promised return value could be a Promise…..
  3. Call the Promise’s resolve method, and what if the argument is A Promise (this is not in the A+ specification, but the new Promise does).
  4. Other people may not implement the specification, so our resolvePromise needs to be limited. Once the state changes, it cannot change again. (This is in the A+ specification test case, but I don’t think it makes much sense.)

4.1.1 Default Parameters

Promise. Resolve (1). Then (). Then (). Then (). Then ((val) = > {the console. The log (val) / / 1}) / / failure are similarCopy the code

You can pass a callback function by default

then(onFufilled,onRejected){ onFufilled = typeof onFufilled === 'function'? onFufilled:value=>value; . }Copy the code

In 4.1.2 x promise is nested

This isn’t too hard, either. Recursively call resolvePromise to resolve

let resolvePromise = (promise2,x,resolve,reject) => { ... Then = x. hen /* Then. Call (x,resolve,reject) * is the same as then. Call (x,(y)=>{* resolve(y) Now this may be a promise, so recursively call resolvePromise to resolve *},reject) */  then.call(x,(y)=>{ resolvePromise((promise2,y,resolve,reject) },reject) ... }Copy the code

4.1.3 Resolve is a promise

constructor(executor){ ... Let resolve = (value) =>{// if(value instanceof promise){// if(value instanceof promise){ Then (resolve,reject) return value. Then (resolve,reject)}}...Copy the code

5. Add methods

There are five key Promise methods

5.1 Promise. Resovle

Packaging an object into a Promise object, pay special attention to the state is not necessarily successful various notes please see Ruan Yifeng ES6-PROMISE direct memory is not good memory, but the combination of source code is very simple, of course

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

5.2 Promise. Reject

The promise.Reject (Reason) method also returns a new Promise instance with a state of Rejected.

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

5.3 PromiseInstance. Prototype. Finally

This is an instance method, and the others are class methods that will be called on success or failure, so it returns a Promimse as well. Success and failure will call the callback passed in. Finally does not accept a value. The state of the returned Promise is affected by the previous Promise state. Finally If a callback returns a Promise in the middle at the same time, it waits for the Promise

Promise.resolve(1).finally( (a)=>{ return new Promise((resolve)=>{ setTimeout(function () { resolve(2) },3000) }) } ). Then ((data)=>{console.log(data)}) Wait 3 seconds and print 1Copy the code

The finally realize

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};
Copy the code

5.4 Promise. Race Promise. All

Race and all are the ones that call first and execute the callbacks in the later THEN, and all are the ones that execute the callbacks in the later THEN and they both need to iterate over the array that was passed in as an argument

The implementation of all requires a counter, which is a way of implementing asynchronous task notifications either directly or asynchronously increments the counter by one when the counter is equal to the array length, and then passes the resulting array to the next callback

The implementation of race is to iterate through the current element of the array to change the value that returns a promise, and whoever changes first takes the value and passes it to the function

return promose((resolve,reject)=>{
if(isPromise(current)){
                current.then(resolve,reject)
            }else{
                resolve(current)
            }
})

Copy the code

See 6 for concrete implementation

6. Complete implementation

let resolvePromise = (promise2,x,resolve,reject) => { if(promise2 === x){ return reject(new TypeError('Chaining cycle Detected for promise #< promise >')} // If a call fails, it cannot be called successfully. if(typeof x ==='function' || (typeof x === 'object' && x! == null) ){ try{ let then = x.then; // Object,dedefineProperty if(typeof then === 'function'){ then.call(x,(y)=>{ // x.then(y=>,err=>) if(called) return; Call = true // resolve(y) call = true // resolve(y) resolvePromise(promise2,y,resolve,reject); },e=>{if(called) return; called = true reject(e); }) }else{ if(called) return; called = true resolve(x); } }catch(e){ if(called) return; called = true reject(e); } }else{ if(called) return; called = true resolve(x); // '123' 123 } } class Promise{ constructor(executor){ this.value = undefined; this.reason = undefined; this.status = 'pending'; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; Let the resolve = (value) = > {/ when/if the value of the resolve a promise / / if (typeof value = = = 'function' | | (typeof value = = 'object' && value ! == null)){ // if(typeof value.then == 'function'){ // return value.then(resolve,reject) // } // } if(value instanceof {// I'll make this Promise work, } if(this.status === 'pending'){this.status = 'pending' 'fulfilled' this.value = value; this.onResolvedCallbacks.forEach(fn=>fn()); } } let reject = (reason) =>{ if(this.status === 'pending'){ this.status = 'rejected' this.reason = reason; this.onRejectedCallbacks.forEach(fn=>fn()); } } try{ executor(resolve,reject); }catch(e){ console.log(e) reject(e); }} then(onFufilled,onRejected){// onFufilled = typeof onFufilled === 'function'? onFufilled:value=>value; onRejected = typeof onRejected === 'function'? onRejected:err=>{throw err} let promise2 = new Promise((resolve,reject)=>{ if(this.status === 'fulfilled'){ SetTimeout (()=>{// To ensure that a try{let x = onFufilled(this.value); resolvePromise(promise2,x,resolve,reject); }catch(e){ console.log(e); reject(e); } }) } if(this.status === 'rejected'){ setTimeout(() => { try{ let x= onRejected(this.reason); resolvePromise(promise2,x,resolve,reject); }catch(e){ reject(e); }}); } if(this.status === 'pending'){ this.onResolvedCallbacks.push(()=>{ setTimeout(() => { try{ let x = onFufilled(this.value); resolvePromise(promise2,x,resolve,reject); }catch(e){ reject(e); }})}); this.onRejectedCallbacks.push(()=>{ setTimeout(() => { try{ let x= onRejected(this.reason); resolvePromise(promise2,x,resolve,reject); }catch(e){ reject(e); }}); }); } }) return promise2 } finally(callback){ let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); Return this. Then (null,errCallback)} static resolve(value){return new Promise((resolve,reject)=>{ resolve(value); }) } static reject(err){ return new Promise((resolve,reject)=>{ reject(err); }) } static race(values){ return new Promise((resolve,reject)=>{ for(let i = 0 ; i<values.length; i++){ let current = values[i]; if(isPromise(current)){ current.then(resolve,reject) }else{ resolve(current) } } }) } static all(values){ return new Promise((resolve,reject)=>{ let arr = []; // let I = 0; function processData(key,val) { arr[key] = val; if(++i == values.length){ resolve(arr); } } for(let i = 0 ; i<values.length; i++){ let current = values[i]; if(isPromise(current)){ current.then(y=>{ processData(i,y); },reject) }else{ processData(i,current); }}}}}) Promise. Deferred () = = > {/ / test method DFD = {}; dfd.promise = new Promise((resolve,reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }) return dfd; Resolve, reject, reject} module.exports = promise; // Promises -aplus-tests promise.js // Promises -aplus-tests promise.js // Promises -aplus-tests promise.js // Promises -aplus-tests promise.js // Promises -aplus-tests promise.js // Promises -aplus-tests promise.js // Promises -aplus-tests promise.js //Copy the code

7. The interview questions

7.1 Please write the following code operation results

Promise.reject(1).then().finally( (a)=>{ console.log('a:'a) //undefined setTimeout(function () { console.log(2) },3000) }). Then ((data) = > {the console. The log (3) the console. The log (data)}, (e) = > {the console. The log (' error '+ e) / / print error1}) / /Copy the code

A :undefined error1 > 2 seconds 2

7.2 Is the Promise constructor synchronous or asynchronous, then method

Synchronous, asynchronous source code written very clearly

7.3 Simulate the implementation of a promise.finally

The answer:

Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};
Copy the code

7.4 This section describes the use, principle and error handling of promose. all

Use: Need to fetch more than one thing at a time before performing a callback Principle: Return a Promise: P iterates through the array of parameters, if it’s not a Promise, add it directly to the arR of the result array. Counter ++ If it’s a Promise, wait until the promise executes and then add the result to the array counter ++ counter === array length. Error handling: p.object (e)

7.5 Design and implement promise.race

The answer:

Promise._race = promises => new Promise((resolve, reject) => {
    promises.forEach(promise => {
        promise.then(resolve, reject) 
    })
})
Copy the code

8 summarizes

Summarized the Promise of the realization, as well as the interview common test points, I believe that if all understand, interview to ask Promise certainly can add a lot of points. Due to limited technology, if you find any mistakes in reading, please point them out in the comments.

Xiaobian opened a point-by-point breakthrough series, an article about a knowledge point, learning to system, knowledge points also need to be summarized, the article will also include common related interview questions, so this series also please support a lot!

9 the last

The article appeared in the interview questions have not seen the addiction can[Click here]Free access to the full version of the front end test analysis PDF oh!

If you found this article helpful and enjoyed the series, please let me know in the comments. Your support is my biggest motivation!