Три брата в Promise .all(), .race(), .allSettled()

JavaScript ECMAScript 6

Автор: д-р Аксель Раушмайер Переводчик: Front-end Xiaozhi Источник: 2ality

Ставь лайк и смотри, поиск в WeChat【Переезд в мир】Обратите внимание на этого человека, который не имеет большого фабричного прошлого, но имеет восходящий и позитивный настрой. эта статьяGitHub GitHub.com/QQ449245884…Он был включен, статьи были классифицированы, и многие мои документы и учебные материалы были систематизированы.

Все говорили, что нет проекта для написания резюме, поэтому я помог вам найти проект, и это было с бонусом.【Учебник по строительству】.

Начиная с ES6, мы в основном используемPromise.all()а такжеPromise.race(),Promise.allSettled()Предложение достигло стадии 4, поэтому оно будетECMAScript 2020часть.

1 Обзор

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

  • Promise.all(iterable)метод возвращаетPromiseэкземпляр, этот экземпляр находится вiterableвсе параметрыpromise"разрешены" или не включены в параметрpromiseКогда обратный вызов завершается (разрешить); если параметрpromiseПроизошел сбой (отклонен), обратный вызов этого экземпляра не удался (отклонен), причиной сбоя является первый сбойpromiseрезультат

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

  • Promise.race(iterable)метод возвращаетpromise, как только один из итераторовpromiseразрешено или отклонено, возвращеноpromiseбудут решены или отклонены.

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

  • Метод **Promise.allSettled()** возвращаетpromise,Долженpromiseво всех данныхpromiseанализируется после того, как он был проанализирован или отклонен, и каждый объект описывает каждыйpromiseрезультат.

Резюме: статус обещания

с учетом возвращенияPromiseасинхронные операции, следующиеPromiseвозможные состояния:

  • pending: начальное состояние, ни состояние успеха, ни состояние отказа.
  • выполнено: означает, что операция завершена успешно.
  • отклонено: означает, что операция не удалась.
  • Урегулировано:PromiseЛибо завершено, либо отклонено.PromiseОднажды достигнутое, его состояние никогда не меняется.

3. Что такое комбинация

Также известный как шаблон «часть-целое», он объединяет объекты в древовидную структуру для представления иерархии «часть-целое». Шаблон композиции делает использование отдельных объектов и объектов композиции согласованным и основан на двух функциях:

  • Примитивные функции (сокращенно примитивы) создают атомарные блоки.
  • Композиционная функция (сокращенно: композиция) объединяет атомы и/или композиты в композит.

Для обещаний JS

  • К примитивным функциям относятся:Promise.resolve(),Promise.reject()

  • Объединение функций:Promise.all(), Promise.race(), Promise.allSettled()

4. Promise.all()

Promise.all()Сигнатура типа:

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

Ситуация возврата:

Выполнение:Если переданный в iterable пустой,Promise.allсинхронно вернет завершенный (resolved) положение делpromise. Если все входящиеpromiseстановятся полными, или переданный в iterable не содержитpromise,Promise.allвернутьpromiseСтановится полным асинхронно. При любых условиях,Promise.allвернутьpromiseРезультатом статуса завершения является массив, который содержит все значения, переданные в объект параметра итерации (в том числе не обещанные значения).

Отказ/отказ:Если входящийpromiseодин неудачник(rejected),Promise.allАсинхронно отправляет результат сбоя в функцию обратного вызова состояния сбоя, независимо от другихpromiseзавершено.

Вот пример:

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

Что делать, если одно из обещаний отклонено:

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

Описание рисунка нижеPromise.all()как это работает

这需要有数字电路的知识才能看得懂哦

4.1 Асинхронный .map() и Promise.all()

методы преобразования массива, такие как.map(),.filter()д., для синхронных вычислений. Например

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

если.map()Обратный вызов основан наPromiseЧто происходит с функцией ? использовать этот путь.map()Возвращаемый результат представляет собойPromisesмножество.

PromisesМассивы не являются данными, которые может использовать обычный код, но мы можемPromise.all()для решения этой проблемы: он преобразует массив Promises вPromiseи реализуется с использованием массива простых значений.

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]);
  });

Более практический рабочий пример с .map()

Далее мы используем.map()а такжеPromise.all()отWebзагрузить файл. Во-первых, нам понадобятся следующие вспомогательные функции:

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

downloadText()использовать на основеPromiseFetch API загружает файлы в виде потока строк:

  • Во-первых, он получает ответ асинхронно (строка A).

  • response.ok (строка B) проверяет наличие ошибок типа «файл не найден».

  • Если ошибок нет, используйте.text()(Строка C) Получить содержимое файла в виде строки.

В примере ниже мы скачали два файла

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!']
    ));

Упрощенная реализация 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);
  });
}

##5. Promise.race()

Promise.race()Определение метода:

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

Promise.race(iterable)метод возвращаетpromise, как только один из итераторовpromiseразрешено или отклонено, возвращеноpromiseбудут решены или отклонены. Вот несколько примеров:

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'));

вAРяд,Promiseявляется состоянием завершения, поэтому первыйCГильдия выполнит (хотя первыйBстрока отклонена).

Давайте посмотрим, что произойдет, если обещание будет отклонено первым:

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'));

Обратите внимание, что из-заPromseбыл отвергнут первым, поэтомуPromise.race()вернул отклоненныйPromise

это означаетPromise.race([])Результат никогда не завершается.

На рисунке ниже показаноPromise.race()Как это работает:

这需要有数字电路的知识才能看得懂

Promise.race(), когда время обещания истекает

В этом разделе мы будем использоватьPromise.race()для обработки тайм-аутовPromise. Следующие вспомогательные функции:

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

resolveAfter()Главное вернуть статус какresolveизPromise, значение - входящийvalue

Вызовите вышеуказанный метод:

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

timeout()вернутьPromise,ДолженPromiseСостояние зависит от входящегоpromiseусловие .

вtimeoutв функцииresolveAfter(timeoutInMs, Promise.reject(new Error('Operation timed out')),пройти черезresolveAfterКак видно из определения, возвращаемый результат имеет отклоненный статус.Promise.

посмотри сноваtimeout(timeoutInMs, promise)операции. Если входящийpromiseКогда статус завершен до указанного времени,timeoutВозвращаемый результат - завершенный статусPromise, в состоянии пройти.thenПервый параметр обратного вызова обрабатывает возвращаемый результат.

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

И наоборот, если это делается после указанного времени, простоtimeoutВозвращаемый результат - статус отклоненияPromise, что вызываетcatchФункция обратного вызова, указанная методом.

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

Важно понимать, что на самом деле означает «Тайм-аут обещания»:

  1. Если входящийPromiseЕсли это разрешено, результат будет возвращен вPromise.
  2. Если решение не будет решено достаточно быстро, выводPromiseСтатус Отказано.

То есть тайм-аут блокирует только входящие промисы, влияя на выходные промисы (поскольку промисы могут быть разрешены только один раз), но не блокирует входящие промисы.Promiseасинхронная работа.

5.2 Упрощенная реализация Promess.race ()

Ниже приведеныPromise.race()Упрощенная реализация (не выполняет проверки безопасности)

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;
  });
}

6.Promise.allSettled()

“Promise.allSettled”Эта особенность обусловленаJason Williams,Robert Pamelyа такжеMathias Bynensпредложить.

promise.allsettle()Определение метода:

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

он возвращаетArrayизPromise, элементы которого имеют следующие характеристики типа:

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

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

interface RejectionObject {
  status: 'rejected';
  reason: unknown;
}

Promise.allSettled()Метод возвращает обещание, которое разрешается после того, как все заданные обещания были разрешены или отклонены, и каждый объект описывает результат каждого обещания.

Например, например, каждый пользователь одновременно заполняет три отдельные формы на странице.Эти три формы отправляются на сервер в трех интерфейсах.Три интерфейса независимы и не имеют зависимости от порядка.На данный момент нам нужно дождаться завершения всех запросов Ситуация с запросом пользователя на отправку формы

в несколькихpromiseВ то же время мы скоро подумаем об использованииPromise.allдля упаковки, но из-заPromise.allФункция короткого замыкания , если какая-либо из трех отправок не удалась, последующая форма не будет отправлена, что не соответствует нашим потребностям.

Promise.allSettledа такжеPromise.allТочно так же его параметр принимаетPromiseмассив , возвращает новыйPromise, отличие только в том, что он не закорачивает, т.е.PromiseПосле того, как вся обработка завершена, мы можем получить каждыйPromiseстатус, независимо от того, был он успешно обработан или нет.

Описание рисунка нижеpromise.allsettle()как это работает

这需要有数字电路的知识才能看得懂哦

6.1 Пример Promise.allSettled()

ЭтоPromise.allSettled()Быстрый демонстрационный пример использования

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

6.2 Более сложный пример Promise.allSettled()

Этот пример похож на.map()а такжеPromise.all()Пример (который мы позаимствовали изdownloadText()функция): мы загружаем несколько текстовых файлов,urlхранится в массиве. Однако на этот раз вместо остановки при ошибке мы хотим продолжить выполнение.Promise.allSettled()Позвольте нам сделать это:

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'),
      },
    ]
));

6.3 Упрощенная реализация Promise.allSettled()

Этоpromise.allsettle()Упрощенная реализация (без выполнения проверок безопасности)

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);
  });
}

7. Характеристики короткого замыкания

Promise.all()а такжеromise.race()иметь характеристики короткого замыкания

  • Promise.all(): если параметрpromiseПроизошел сбой (отклонен), обратный вызов этого экземпляра не выполнен (отклонен)

Promise.race(): если один из параметровpromiseРазрешено или отклонено, возвращенное обещание разрешено или отклонено.

8. Параллелизм и Promise.all()

8.1 Последовательное и параллельное выполнение

Рассмотрим следующий код:

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

использовать.then()Последовательное выполнение основано наPromiseфункция: только еслиasyncFunc1()выполняется после получения результатаasyncFunc2().

а такжеPromise.all()выполняются одновременно

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

9.2 Уловки параллелизма: сосредоточьтесь на том, когда начинаются операции

Советы по идентификации параллельного асинхронного кода: сосредоточьтесь на том, когда запускаются асинхронные операции, а не на том, как они обрабатываютсяPromises.

Например, каждая функция ниже выполняется одновременноasyncFunc1()а такжеasyncFunc2(), потому что они начинаются примерно в одно и то же время.

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

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

С другой стороны, следующие две функции выполняются последовательноasyncFunc1()а такжеasyncFunc2(): asyncFunc2()только приasyncFunc1()вызывается только после разрешения решения.

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]);
}

9.3 Promise.all() и Fork-Join Программирование разделяй и властвуй

Promise.all()Слабо связано с режимом параллелизма «форк-соединение». Вернемся к нашему предыдущему примеру:

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!']
    ));
  • Вилка: вAline, разделяет две асинхронные задачи и выполняет их одновременно.
  • ПрисоединяйсяBВ строке суммируются результаты, полученные по каждой малой задаче.

Ошибки, которые могут существовать после развертывания кода, не могут быть известны в режиме реального времени.Чтобы решить эти ошибки впоследствии, много времени тратится на отладку журнала.Кстати, я рекомендую всем полезный инструмент мониторинга ошибок.Fundebug.

оригинал:2 али ненавидит.com/2019/08/pro…

общаться с

Статья постоянно обновляется каждую неделю. Вы можете выполнить поиск «Big Move to the World» в WeChat, чтобы прочитать и обновить ее как можно скорее (на одну или две статьи раньше, чем в блоге). Эта статья находится на GitHub.GitHub.com/QQ449245884…Он был включен, и многие мои документы были разобраны. Добро пожаловать в Звезду и совершенство. Вы можете обратиться в тестовый центр для ознакомления во время собеседования. Кроме того, обратите внимание на паблик-аккаунт и ответьте в фоновом режиме.Благосостояние, вы можете увидеть преимущества, вы знаете.