Author: Dr. Axel Rauschmayer

Click “like” and then look, wechat search [Big Move the world] pay attention to this person without dACHang background, but with a positive attitude upward. In this paper, making github.com/qq449245884… Has been included, the article has been categorized, also organized a lot of my documentation, and tutorial materials.

Everyone said there was no project on your resume, so I found one and gave it away【 Construction tutorial 】.

Since ES6, we have mostly used Promise.all() and promise.race (). The Promise.AllSettled () proposal has reached phase 4 and will therefore be part of ECMAScript 2020.

1. An overview of the

Promise.all(promises: Iterable<Promise>): Promise<Array>

  • Promise.all(iterable)Method returns aPromiseInstance, which is in the可迭代All within the parameterspromiseAre ‘resolved’ or not included in the parameterspromiseWhen the callback completes (resolve); If the parameterpromiseThere is a callback rejected, which fails because of the first failurepromiseThe results of the

Promise.race(promises: Iterable<Promise>): Promise

  • Promise.race(iterable)Method returns apromise, once one of the iteratorspromiseResolved or rejected, returnedpromiseIt will be resolved or rejected.

Promise.allSettled(promises: Iterable<Promise>): Promise<Array<SettlementObject>>

  • The ** promise.allSettled ()** method returns onepromisethepromiseIn all givenpromiseParsed or rejected, and each object describes eachpromiseResults.

Review: Promise state

Given an asynchronous operation that returns a Promise, these are the possible states of the Promise:

  • Pending: The initial state, which is neither successful nor failed.
  • This is a pity: which means that the operation will be completed successfully.
  • Rejected: Indicates that the operation fails.
  • The hills:PromiseIt’s either done or rejected.PromiseOnce achieved, its state does not change.

3. What is a combination

Also known as the partial-whole pattern, objects are consolidated into a tree structure to represent a partial-whole hierarchy. The composite pattern makes the use of single objects and composite objects consistent. It is based on two functions:

  • Primitive functions (short: primitive) create atomic blocks.
  • Combinatorial functions combine atoms and/or composite components to form a composite.

For JS Promises

  • Primitive functions include: promise.resolve (), promise.reject ()

  • Combinatorial functions: promise.all (), promise.race (), promise.allSettled ()

4. Promise.all()

Promise.all() type signature:

  • Promise.all(promises: Iterable<Promise>): Promise<Array>

Return condition:

Fulfillment: If the iterable passed in is empty, promise.all returns a Promise in a completed state synchronously. The promise returned by promise.all becomes completed asynchronously if all incoming promises become complete, or if there are no promises in the incoming iterable. In any case, the result of the completion state of the Promise returned by promise.all is an array containing the values of all the passed iteration parameter objects (including non-Promise values).

Rejection: If a promise is passed in with a failed promise, promise. all asynchronously gives that failed result to the failed state callback, regardless of whether the other promise is fulfilled or not.

Here’s an example:

const promises = [
  Promise.resolve('a'),
  Promise.resolve('b'),
  Promise.resolve('c'),
];
Promise.all(promises)
  .then((arr) => assert.deepEqual(
    arr, ['a', 'b', 'c']
  ));
Copy the code

What happens if one of these promises is rejected:

const promises = [
  Promise.resolve('a'),
  Promise.resolve('b'),
  Promise.reject('ERROR'),
];
Promise.all(promises)
  .catch((err) => assert.equal(
    err, 'ERROR'
  ));
Copy the code

How does promise.all () work

4.1 Asynchrony.map() and promise.all ()

Array conversion methods, such as.map() and.filter(), are used for synchronous calculation. For example,

function timesTwoSync(x) {
  return 2 * x;
}
const arr = [1, 2, 3];
const result = arr.map(timesTwoSync);
assert.deepEqual(result, [2, 4, 6]);
Copy the code

What happens if the callback to.map() is based on a Promise function? Using this method. Map () returns a Promises array.

Promises array is not data that ordinary code can use, but we can solve this problem with promise.all () : It converts Promises array into Promises and uses a set of ordinary values to do so.

function timesTwoAsync(x) {
  return new Promise(resolve => resolve(x * 2));
}
const arr = [1, 2, 3];
const promiseArr = arr.map(timesTwoAsync);
Promise.all(promiseArr)
  .then(result => {
    assert.deepEqual(result, [2, 4, 6]);
  });
Copy the code

More practical working on the.map() example

Next, let’s download the file from the Web using.map() and promise.all (). First, we need the following help functions:

function downloadText(url) { return fetch(url) .then((response) => { // (A) if (! response.ok) { // (B) throw new Error(response.statusText); } return response.text(); // (C) }); }Copy the code

DownloadText () downloads files as a string stream using the Promise-based FETCH API:

  • First, it retrieves the response asynchronously (line A).

  • Response.ok (line B) checks for errors such as “file not found”.

  • If there are no errors, use.text()(line C) to retrieve the contents of the file as a string.

In the example below, we downloaded two files

const urls = [
  'http://example.com/first.txt',
  'http://example.com/second.txt',
];

const promises = urls.map(
  url => downloadText(url));

Promise.all(promises)
  .then(
    (arr) => assert.deepEqual(
      arr, ['First!', 'Second!']
    ));
Copy the code

A simplified implementation of promise.all ()

function all(iterable) {
  return new Promise((resolve, reject) => {
    let index = 0;
    for (const promise of iterable) {
      // Capture the current value of `index`
      const currentIndex = index;
      promise.then(
        (value) => {
          if (anErrorOccurred) return;
          result[currentIndex] = value;
          elementCount++;
          if (elementCount === result.length) {
            resolve(result);
          }
        },
        (err) => {
          if (anErrorOccurred) return;
          anErrorOccurred = true;
          reject(err);
        });
      index++;
    }
    if (index === 0) {
      resolve([]);
      return;
    }
    let elementCount = 0;
    let anErrorOccurred = false;
    const result = new Array(index);
  });
}
Copy the code

##5. Promise.race()

The definition of the promise.race () method:

Promise.race(promises: Iterable<Promise>): Promise

The promise.race (iterable) method returns a Promise that is resolved or rejected once a Promise in the iterator is resolved or rejected. Here are some examples:

const promises = [
  new Promise((resolve, reject) =>
    setTimeout(() => resolve('result'), 100)), // (A)
  new Promise((resolve, reject) =>
    setTimeout(() => reject('ERROR'), 200)), // (B)
];
Promise.race(promises)
  .then((result) => assert.equal( // (C)
    result, 'result'));
Copy the code

At line A, the Promise is in the completed state, so line C is executed (although line B is rejected).

If the Promise is rejected first, let’s see what happens:

const promises = [
  new Promise((resolve, reject) =>
    setTimeout(() => resolve('result'), 200)),
  new Promise((resolve, reject) =>
    setTimeout(() => reject('ERROR'), 100)),
];
Promise.race(promises)
  .then(
    (result) => assert.fail(),
    (err) => assert.equal(
      err, 'ERROR'));
Copy the code

Note that since Promse was rejected first, promise.race () returns a rejected Promise

This means that the result of promise.race ([]) will never complete.

The following diagram illustrates how promise.race () works:

Promise.race() in the case of a Promise timeout

In this section, we will use promise.race () to handle timed promises. The following auxiliary functions:

function resolveAfter(ms, value=undefined) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(value), ms);
  });
}
Copy the code

ResolveAfter () returns a Promise in the resolve state of the incoming value within the specified time

Call the above method:

function timeout(timeoutInMs, promise) {
  return Promise.race([
    promise,
    resolveAfter(timeoutInMs,
      Promise.reject(new Error('Operation timed out'))),
  ]);
}
Copy the code

Timeout () returns a Promise whose state depends on the incoming Promise state.

Where, resolveAfter(timeoutInMs, promise.reject (new Error(‘Operation timed out’))) in the timeout function can be known from resolveAfter definition, The result returns a Promise of a rejected state.

Take a look at how timeout(timeoutInMs, Promise) works. If an incoming promise is in a completed state before the specified time, the result returned by timeout is a completed state promise, which can be processed by the first callback argument to the.then.

timeout(200, resolveAfter(100, 'Result! ')) .then(result => assert.equal(result, 'Result! '));Copy the code

In contrast, if the completion is done after the specified time, the result returned by timeout is a Promise that rejects the state, triggering the callback specified by the catch method.

timeout(100, resolveAfter(2000, 'Result! ')) .catch(err => assert.deepEqual(err, new Error('Operation timed out')));Copy the code

It’s important to understand what “Promise timeout” really means:

  1. If it comes inPromiseThe result will be returned when the lower one is resolvedPromise.
  2. If not solved fast enough, outputPromiseIs in the rejected state.

That is, the timeout only blocks incoming promises, affecting the output of promises (because promises can only be resolved once), but it does not prevent the asynchronous operation of incoming promises.

5.2 A simplified implementation of promise.race ()

Here is a simplified implementation of promise.race () (which does not perform security checks)

function race(iterable) {
  return new Promise((resolve, reject) => {
    for (const promise of iterable) {
      promise.then(
        (value) => {
          if (settlementOccurred) return;
          settlementOccurred = true;
          resolve(value);
        },
        (err) => {
          if (settlementOccurred) return;
          settlementOccurred = true;
          reject(err);
        });
    }
    let settlementOccurred = false;
  });
}
Copy the code

6.Promise.allSettled()

The feature “Promise.allSettled” was developed by Jason Williams, Robert Pamely, and Mathias Bynens.

Definition of the promise.allsettle() method:

  • Promise.allSettled(promises: Iterable<Promise>) : Promise<Array<SettlementObject>>

It returns an Array Promise whose elements have the following type characteristics:

type SettlementObject<T> = FulfillmentObject<T> | RejectionObject;

interface FulfillmentObject<T> {
  status: 'fulfilled';
  value: T;
}

interface RejectionObject {
  status: 'rejected';
  reason: unknown;
}
Copy the code

The promise.allSettled () method returns a Promise that is resolved after all given promises have been resolved or rejected, and each object describes the outcome of each Promise.

For example, if you fill in three independent forms at the same time on the page, the three forms are submitted to the back end by three interfaces. The three interfaces are independent and do not depend on the order. At this time, we need to wait until all the requests are completed to give the user a reminder of the form submission

When multiple promises are made at the same time, we will soon think of using promise. all for packaging. However, due to the short-circuit characteristic of Promise.all, if any of the previous three submissions fails, the following form will not be submitted, which does not meet our needs.

Promise.allsettled is similar to promise.all, in that it accepts an array of promises and returns a new Promise, except that it will not be short-circuited. This means that we can get the state of each Promise after it has been processed, regardless of whether it has been processed successfully or not.

How does promise.allsettle() work

6.1 Promise. AllSettled () example

This is a quick demonstration of how Promise.allSettled() is used

Promise.allSettled([
  Promise.resolve('a'),
  Promise.reject('b'),
])
.then(arr => assert.deepEqual(arr, [
  { status: 'fulfilled', value:  'a' },
  { status: 'rejected',  reason: 'b' },
]));
Copy the code

6.2 Promise.AllSettled () a more complex example

This example is similar to the.map() and promise.all () examples (from which we borrowed the downloadText() function): we download multiple text files whose urls are stored in an array. But this time, we don’t want to stop when something goes wrong, we want to continue. Promise.allsettled () allows us to do this:

const urls = [
  'http://example.com/exists.txt',
  'http://example.com/missing.txt',
];

const result = Promise.allSettled(
  urls.map(u => downloadText(u)));
result.then(
  arr => assert.deepEqual(
    arr,
    [
      {
        status: 'fulfilled',
        value: 'Hello!',
      },
      {
        status: 'rejected',
        reason: new Error('Not Found'),
      },
    ]
));
Copy the code

6.3 Simplified implementation of Promise.allSettled()

This is a simplified implementation of promise.allsettle() (does not perform security checks)

function allSettled(iterable) {
  return new Promise((resolve, reject) => {
    function addElementToResult(i, elem) {
      result[i] = elem;
      elementCount++;
      if (elementCount === result.length) {
        resolve(result);
      }
    }

    let index = 0;
    for (const promise of iterable) {
      // Capture the current value of `index`
      const currentIndex = index;
      promise.then(
        (value) => addElementToResult(
          currentIndex, {
            status: 'fulfilled',
            value
          }),
        (reason) => addElementToResult(
          currentIndex, {
            status: 'rejected',
            reason
          }));
      index++;
    }
    if (index === 0) {
      resolve([]);
      return;
    }
    let elementCount = 0;
    const result = new Array(index);
  });
}
Copy the code

7. Short circuit characteristics

Promise.all() and Romise.race () both have short circuit characteristics

  • Promise.all(): If the parameter is inpromiseThere is a reject, this instance callback fails (Reject)

Promise.race() : If one of the promises in the argument resolves or rejects, the returned Promise resolves or rejects.

8. Concurrency and promise.all ()

8.1 Sequential Execution and Concurrent Execution

Consider the following code:

asyncFunc1()
  .then(result1 => {
    assert.equal(result1, 'one');
    return asyncFunc2();
  })
  .then(result2 => {
    assert.equal(result2, 'two');
  });
Copy the code

Promise based functions are executed using.then() order: asyncFunc2() is executed only after the result of asyncFunc1() has been resolved.

Promise.all() is executed concurrently

Promise.all([asyncFunc1(), asyncFunc2()])
  .then(arr => {
    assert.deepEqual(arr, ['one', 'two']);
  });
Copy the code

9.2 Concurrency Technique: Notice when an operation starts

Tips for determining concurrent asynchronous code: Focus on when asynchronous operations start, not how to handle Promises.

For example, each of the following functions executes asyncFunc1() and asyncFunc2() at the same time, because they start at about the same time.

function concurrentAll() {
  return Promise.all([asyncFunc1(), asyncFunc2()]);
}

function concurrentThen() {
  const p1 = asyncFunc1();
  const p2 = asyncFunc2();
  return p1.then(r1 => p2.then(r2 => [r1, r2]));
}
Copy the code

On the other hand, the following two functions execute asyncFunc1() and asyncFunc2() in turn: asyncFunc2() is called only after asyncFunc1() has been resolved.

function sequentialThen() {
  return asyncFunc1()
    .then(r1 => asyncFunc2()
      .then(r2 => [r1, r2]));
}

function sequentialAll() {
  const p1 = asyncFunc1();
  const p2 = p1.then(() => asyncFunc2());
  return Promise.all([p1, p2]);
}
Copy the code

9.3 Promise.all() and fork-join divide and conquer programming

Promise.all() is loosely related to the concurrency pattern “fork join.” Let’s go back to our previous example:

Promise.all([
    // (A) fork
    downloadText('http://example.com/first.txt'),
    downloadText('http://example.com/second.txt'),
  ])
  // (B) join
  .then(
    (arr) => assert.deepEqual(
      arr, ['First!', 'Second!']
    ));
Copy the code
  • The Fork:ALine, split two asynchronous tasks and execute them simultaneously.
  • The Join:BLine, the results of each small task are summarized.

The bugs that may exist after code deployment cannot be known in real time. In order to solve these bugs, I spent a lot of time on log debugging. Incidentally, I recommend a good BUG monitoring tool for youFundebug.

Original text: 2 ality.com/2019/08/pro…

communication

This article is updated every week, you can search wechat “big move the world” for the first time to read and urge more (one or two earlier than the blog hey), this article GitHub github.com/qq449245884… It has been included and sorted out a lot of my documents. Welcome Star and perfect. You can refer to the examination points for review in the interview.