preface

Jin SAN Yin Si, as a senior student who is about to become a graduate, I can’t wait to put my resume into byte’s flying book department. I thought about weighing a few pounds, but I didn’t think it was revealed 🤣, and my dream of going to big factory fell down on promsie.all. But young people should always have the will to fight, from where to climb up! The following is the recheck time.

What is a Promise. All?

Promise.all is a method on an ES6 Promise object that wraps multiple Promise instances into a single Promise instance. Here’s how MDN describes Promise.all:

  • The promise.all () method accepts an iterable of promises. Array, Map, and Set are all ES6 iterable types), and only one Promise instance is returned. The result of the resolve callbacks to all of those entered promises is an Array. The Promise’s resolve callback is executed when all the incoming Promise’s resolve callbacks have ended, or when no promises are left in the input iterable. Its Reject callback execution throws an error as soon as any incoming promise’s Reject callback is executed or an invalid promise is entered, and reject is the first error message thrown.

I put on my 300-degree myopic glasses and carefully extracted the key words in this description:

  1. Promise.allThe return value of is a newPromiseInstance.
  2. Promise.allAccept a traversable data container in which each element should bePromiseInstance. I’m just saying, let’s say this container is an array.
  3. Each of the arraysPromiseInstances are successful (bypenddingState transition tofulfilledThe state),Promise.allTo success. thesePromiseInstance-ownedresolveThe results are set in the original order in an array asPromise.allresolveResults.
  4. You only need one in the arrayPromiseThe instance failed (bypenddingState transition torejectedThe state),Promise.allWill fail.Promise.all.catch()I’m going to catch thisreject.

Native promise.all tests

Let’s take a look at the original Promise. All.

const p1 = Promise.resolve('p1')

const p2 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('P2 delay one second')},1000)})const p3 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('P3 delay of two seconds')},2000)})const p4 = Promise.reject('p4 rejected')

const p5 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject('P5 Rejected delay 1.5sec')},1500)})// All Promise instances succeed
Promise.all([p1, p2, p3])
  .then(res= > {
    console.log(res)
  })
  .catch(err= > console.log(err)) // Prints after 2 seconds ['p1', 'P2 delay 1 second ',' P3 delay 2 seconds']
  
// A Promise instance failed
Promise.all([p1, p2, p4])
  .then(res= > {
    console.log(res)
  })
  .catch(err= > console.log(err)) // p4 rejected
  
// A delayed failed Promise
 Promise.all([p1, p2, p5])
  .then(res= > {
    console.log(res)
  })
  .catch(err= > console.log(err)) // after 1.5 seconds, print p5 Rejected
  
// Two Promise instances failed
Promise.all([p1, p4, p5])
  .then(res= > {
    console.log(res)
  })
  .catch(err= > console.log(err)) // p4 rejected


Copy the code

Pay attention to

P4 and p5 above need to be commented out if promise.all is not passed in, because an instance of a Promise that calls reject will report an error if.catch() is not used to catch an error. But if the Promise instance defines its own.catch, the.catch() method of promise.all is not triggered.

OK, theory exists, practice begins!

Implement promise.all manually

  1. Promise.allAccepts an array and returns a new onePromiseThe instance
Promise.MyAll = function (promises) {
  return new Promise((resolve, reject) = >{})}Copy the code
  1. All in the arrayPromiseThe instances are successful,Promise.allTo success. Not surprisingly, we need an array to collect thisPromiseThe instanceresolveThe results. But there’s an old saying that goes, “Don’t worry about ten thousand, just in case,” in case there’s an element in the array that isn’tPromiseWhat to do — then you have to use itPromise.resolve()Get it done. There’s another question here,PromiseInstances cannot be called directlyresolveThe way. We have to be.then()To collect the results. Be careful to keep the order of the results.
Promise.MyAll = function (promises) {
  let arr = []
  return new Promise((resolve, reject) = > {
    promises.forEach((item, i) = > {
      Promise.resolve(item).then(res= > {
        arr[i] = res
      })
    }) 
  })
}
Copy the code
  1. Will collect the results (arrayarr) as a parameter to the outer layerresolveMethods. Here we certainly have a judgment condition, how to judge allPromiseAnd the examples are all successful? This code is easy for beginners to write (yes, I am 😭) :
if (arr.length === promises.length) resolve(arr)
Copy the code

Zan thinks about what Promise is used for — handling asynchronous tasks. Yeah, a lot of asynchronous tasks take time, and what if the last of those promises gets made first? The arR array has only the last item, and all the items in front are empty. So here we should create a counter that increments each time a Promise instance succeeds:

Promise.MyAll = function (promises) {
  let arr = [],
    count = 0
  return new Promise((resolve, reject) = > {
    promises.forEach((item, i) = > {
      Promise.resolve(item).then(res= > {
        arr[i] = res
        count += 1
        if (count === promises.length) resolve(arr)
      })
    })
  })
}
Copy the code
  1. And then finally dealing with failure, there are two ways to write it, the first way is to use.catch()Method capture failed:
Promise.MyAll = function (promises) {
  let arr = [],
    count = 0
  return new Promise((resolve, reject) = > {
    promises.forEach((item, i) = > {
      Promise.resolve(item).then(res= > {
        arr[i] = res
        count += 1
        if (count === promises.length) resolve(arr)
      }).catch(reject)
    })
  })
}
Copy the code

The second way to write this is to pass a second argument to the.then() method, which is a callback to handle errors:

Promise.MyAll = function (promises) {
  let arr = [],
    count = 0
  return new Promise((resolve, reject) = > {
    promises.forEach((item, i) = > {
      Promise.resolve(item).then(res= > {
        arr[i] = res
        count += 1
        if (count === promises.length) resolve(arr)
      }, reject)
    })
  })
}
Copy the code

The test case

Here’s the Promise. All done, put it to the test (rub your hands) :

const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('P2 delay one second')},1000)})const p3 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('P3 delay of two seconds')},2000)})const p4 = Promise.reject('p4 rejected')

const p5 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject('P5 Rejected delay 1.5sec')},1500)})// All Promsie were successful
Promise.MyAll([p1, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // Prints after 2 seconds ['p1', 'P2 delay 1 second ',' P3 delay 2 seconds']
  
// A Promise fails
Promise.MyAll([p1, p2, p4])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p4 rejected
  
// A delayed failed Promise
Promise.MyAll([p1, p2, p5])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // Print p5 rejected delay 1.5 seconds
 
// Two failed promises
Promise.MyAll([p1, p4, p5])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p4 rejected
Copy the code

“OhOhOhOh~~~~”, and the native promise.all run not exactly like, but exactly the same. Strike while the iron is hot, as the old saying goes. I went to a learning site (MDN Web Docs (mozilla.org)) and saw the methods Promise objects use to handle multiple promises at once: Promise.race, promise.any, promise.allsettle. Since the teacher taught us to draw inferences by analogy, carefully look at the description of the three methods, I really give out 😄.

Promise.race

Promise.race literally means a race, in which the state changes the fastest. The fastest Promise succeeds.

Let’s take a look at the native Promise.race effect

Native promise.race test

const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('P2 delay one second')},1000)})const p3 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('P3 delay of two seconds')},2000)})const p4 = Promise.reject('p4 rejected')

const p5 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject('P5 Rejected delay 1 SEC')},1500)})// P1 has no delay, p2 has 1s delay, p3 has 2s delay
Promise.race([p1, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p1

// p4 reject without delay
Promise.race([p4, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p4 rejected
  
P5 delay 1.5s reject, P2 delay 1s
Promise.race([p5, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // Prints after 1s: P2 delay is 1 second
Copy the code

Theory exists, practice begins

Written Promise. Race

The overall process is similar to Promise, except that the logic of processing the Promise instances in the array is different. Here, we need to take the Promise result that changes the state fastest as the result of promise.race, which is relatively simple, and the code is as follows:

Promise.MyRace = function (promises) {
  return new Promise((resolve, reject) = > {
    // There is no need to use an index, just to loop through each item
    for (const item of promises) {
      Promise.resolve(item).then(resolve, reject)
    }
  })
}
Copy the code

The test case

Or just a few cases, I will not repeat to write 😁

// P1 has no delay, p2 has 1s delay, p3 has 2s delay
Promise.MyRace([p1, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p1

// p4 reject without delay
Promise.MyRace([p4, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p4 rejected
  
P5 delay 1.5s reject, P2 delay 1s
Promise.MyRace([p5, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // Prints after 1s: P2 delay is 1 second
Copy the code

As you can see, the results are consistent with the native promise.race, success!

Promise.any

Promise. Any/all can be seen as opposite. Promise.any will succeed as long as one of the Promise instances succeeds, and promise. any will fail only if all Promise instances fail, at which point promise. any will aggregate all failures/errors together. Returns a failed promise and an instance of type AggregateError. MDN says that this method is still in the experimental stage, if the Node or browser version is too late, it may not work, you can test it yourself.

Native promise.any test

const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('P2 delay one second')},1000)})const p3 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('P3 delay of two seconds')},2000)})const p4 = Promise.reject('p4 rejected')

const p5 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject('P5 Rejected delay 1.5sec')},1500)})// All promises were successful
Promise.any([p1, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p1
  
// Both promises succeed
Promise.any([p1, p2, p4])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p1

// There is only one delayed success Promise
Promise.any([p2, p4, p5])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p2 delay is 1 second

// All promises fail
Promise.any([p4, p5])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // AggregateError: All promises were rejected
Copy the code

If there are multiple successful Promise instances in promise.race, the result that succeeds most quickly is used as the result of resolve.

OK, theory exists, practice begins

Written Promise. Any

  1. Let’s write it out firstPromise.anyOverall structure of:
Promise.MyAny = function (promises) {
  return new Promise((resolve, reject) = > {
    promises.forEach((item, i) = >{})})}Copy the code
  1. Here withPromise.allThe logic is the opposite, we need to collectrejectPromise, also need an array and a counter, use the counter to determine if allPromiseAll instances fail. Also failed in collectionPromiseWe need to put a failure mark on the results so we can analyze them.
Promise.MyAny = function (promises) {
  let arr = [],
    count = 0
  return new Promise((resolve, reject) = > {
    promises.forEach((item, i) = > {
      Promise.resolve(item).then(resolve, err= > {
        arr[i] = { status: 'rejected'.val: err }
        count += 1
        if (count === promises.length) reject(new Error('No promise will succeed'))})})})}Copy the code

Instead of using the AggregateError instance specified on MDN, write by hand as you like and write 😄 as you feel comfortable

The test case

// All promises were successful
Promise.MyAny([p1, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p1
  
// Both promises succeed
Promise.MyAny([p1, p2, p4])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p1

// There is only one delayed success Promise
Promise.MyAny([p2, p4, p5])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // p2 delay is 1 second

// All promises fail
Promise.MyAny([p4, p5])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) // No promise succeeds
Copy the code

Promise.allSettled

Sometimes, our coders have a special need: What if we want a set of Promise instances to wait until they finish asynchronously, whether they succeed or not, and then proceed to the next step? Promise. AllSettled.

nativePromise.allSettledtest

const p1 = Promise.resolve('p1')
const p2 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('P2 delay one second')},1000)})const p3 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('P3 delay of two seconds')},2000)})const p4 = Promise.reject('p4 rejected')

const p5 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject('P5 Rejected delay 1.5sec')},1500)})// All Promise instances succeed
Promise.allSettled([p1, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) 
/ / /
// { status: 'fulfilled', value: 'p1' },
// {status: 'depressing ', value:' P2 delay one second '},
// {status: 'depressing ', value:' P3 delay two seconds'}
// ]

// One Promise failed
Promise.allSettled([p1, p2, p4])
  .then(res= > console.log(res))
  .catch(err= > console.log(err))
/ / /
// { status: 'fulfilled', value: 'p1' },
// {status: 'depressing ', value:' P2 delay one second '},
// { status: 'rejected' , value: 'p4 rejected' }
// ]

// All promises fail
Promise.allSettled([p4, p5])
  .then(res= > console.log(res))
  .catch(err= > console.log(err))
/ / /
// { status: 'rejected', reason: 'p4 rejected' },
// {status: 'rejected', reason: 'p5 Rejected delay 1.5sec '}
// ]
Copy the code

As you can see, like Promise.any, Promise.AllSettled tags all the collected results. The Promise. AllSettled will not become a pity state, no matter how the group of Promise instances are settled, they will become a pity state.

OK, theory exists, practice begins

Written Promise. AllSettled

Resolve all the results of each Promise instance (whether they were successful or not) by using an array. The logic of collecting successful Promise results was implemented in promise. all, and the logic of collecting failed Promise results was dealt with in promise. any. This wave, this wave is doing the same — all the same.

Promise.MyAllSettled = function (promises) {
  let arr = [],
    count = 0
  return new Promise((resolve, reject) = > {
    promises.forEach((item, i) = > {
      Promise.resolve(item).then(res= > {
        arr[i] = { status: 'fulfilled'.val: res }
        count += 1
        if (count === promises.length) resolve(arr)
      }, (err) = > {
        arr[i] = { status: 'rejected'.val: err }
        count += 1
        if (count === promises.length) resolve(arr)
      })
    })
  })
}
Copy the code

This code, although there is no problem logically, but you excellent programmers are certainly not pleasing to the eye, how can there be two sections of repeated code pinch, no, we have to encapsulate.

Promise.MyAllSettled = function (promises) {
  let arr = [],
    count = 0
  return new Promise((resolve, reject) = > {
    const processResult = (res, index, status) = > {
      arr[index] = { status: status, val: res }
      count += 1
      if (count === promises.length) resolve(arr)
    }

    promises.forEach((item, i) = > {
      Promise.resolve(item).then(res= > {
        processResult(res, i, 'fulfilled')},err= > {
        processResult(err, i, 'rejected')})})})}Copy the code

As the saying goes: Take two steps before you get sick. As usual, run a few cases for the code.

The test case

// All Promise instances succeed
Promise.MyAllSettled([p1, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err)) 
/ / /
// { status: 'fulfilled', value: 'p1' },
// {status: 'depressing ', value:' P2 delay one second '},
// {status: 'depressing ', value:' P3 delay two seconds'}
// ]

// There is a MyAllSettled failure
Promise.allSettled([p1, p2, p4])
  .then(res= > console.log(res))
  .catch(err= > console.log(err))
/ / /
// { status: 'fulfilled', value: 'p1' },
// {status: 'depressing ', value:' P2 delay one second '},
// { status: 'rejected' , value: 'p4 rejected' }
// ]

// All MyAllSettled failed
Promise.allSettled([p4, p5])
  .then(res= > console.log(res))
  .catch(err= > console.log(err))
/ / /
// { status: 'rejected', reason: 'p4 rejected' },
// {status: 'rejected', reason: 'p5 Rejected delay 1.5sec '}
// ]
Copy the code

So, done, I can proudly say to my mother: “Mom, I’m not afraid of Promise. All.

conclusion

This bytefly book interview is a great opportunity for me to experience the feeling of being a big company for the first time, and there may be grumpy brother to say: “This is the bytefly interview question? You are the article to deceive praise.” Harm, there is no way, mainly I am too vegetables, from the code I do not know what to now front-end learners, you come to the right week in August, the level is really more times, the interviewer is more kind, did not embarrass me, ask questions are more basic. Thanks to the byte team and the front end for this inclusive and progressive environment. I will sum up this interview well and try my best to improve myself. Come on!

Refer to the article

An interview went cold because I couldn’t fulfill my Promise. All – Nuggets (juejin. Cn)

Promise Objects – Getting Started with ECMAScript 6 (ruanyifeng.com)