Замыкающий Promise.allSerial(), без async-await

#javascript #asynchronous #es6-promise

#javascript #асинхронный #es6-обещание

Вопрос:

Мне нужно выполнить серию асинхронных задач таким образом, чтобы задача выполнялась тогда и только тогда, когда задача, непосредственно предшествующая ей в серии, успешно выполняется. Мне также нужно собрать частичный или полный набор результатов этих задач. Итак, если 3-я задача не удалась, мне нужны результаты первых двух задач.

Теперь я могу очень легко и элегантно использовать стиль обещаний async-await для реализации вышеизложенного и даже могу заставить его работать к моему удовлетворению, но не в следующей версии, отличной от async-await:

   // an async task that fails on an input of 200
  function task(timeout) {
    return new Promise((resolve, reject) => {
      console.log(`Starting a ${timeout}ms task... `);
      if (timeout == 200) {
        console.log(`Starting a ${timeout}ms task... FAILED. timeout = ${timeout}`);
        reject(`task(${timeout}): FAILED`);
      } else {
        setTimeout(() => {
          let result = timeout * 10;
          output.push(result);
          console.log(`Starting a ${timeout}ms task... done. Result: ${result}`);
          resolve();
        }, timeout);
      }
    });
  }

  let input = [400, 300, 200, 100, 50];
  let output = [];

  let p = Promise.resolve(); // initialize
  for (let i = 0; i < input.length;   i) {
    let timeout = input[i];
    p = p
      .then(() => {
        return task(timeout);
      })
      .catch((e) => {
        throw e;
      });
  }

  p = p.finally(() => {
    console.log(`Results so far: ${output.join(", ")}`);
  });
  

Вывод

 Starting a 400ms task... 
Starting a 400ms task... done. Result: 4000
Starting a 300ms task... 
Starting a 300ms task... done. Result: 3000
Starting a 200ms task... 
Starting a 200ms task... FAILED. timeout = 200
Results so far: 4000, 3000
(node:1882) UnhandledPromiseRejectionWarning: task(200): FAILED
(node:1882) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 3)
(node:1882) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
  

Вопрос:

Я очень много обрабатываю отклоненное обещание в a .catch() , но я получаю вышеупомянутое предупреждение. Почему?

Платформа:

Узел: 12.8.3

ОС: Mac OSX 10.14.4

Комментарии:

1. почему вы не хотите использовать async await, если он работает?

2. @Liam Чтобы изучить .then() / .catch() стиль программирования, должен ли я работать с устаревшим кодом.

3. это очень странно @Harry … вы знаете async/await раньше .затем/.catch/.наконец!! замечаю, что это неправильно… но … Я удивлен!

4. @JaromandaX никаких сюрпризов нет. async-await Версия, даже если она компилируется / переносится в .then .catch версию на основе /, более «линейна» для людей (таких как я, исходя из фона, не связанного с обещаниями). Например, когда a reject происходит в async-await основанной реализации, генерируется исключение, которое я могу вырвать try-catch из for цикла. Это также может быть полезно, поскольку тогда мне даже не нужно связывать остальные мои задачи, если задача уже завершилась неудачей. (Представьте себе серию из 10 тысяч задач для аргументации!)

5. но если вы попробуете /finally (без catch), вы также не поймаете никакой ошибки, и будет выдана ошибка, не так ли? так что это то же самое — если вы хотите перехватить ошибку, вы используете catch/.catch

Ответ №1:

 .catch((e) => {
  throw e;
});
  

Это не обрабатывает ошибку… это бросает один!!

Обработайте ошибку p , потому что вы на самом деле не хотите обрабатывать ошибку, task поскольку это означало бы, что задачи, следующие за отклоненной, действительно будут выполняться. Очевидно, вы этого не хотите!

 // an async task that fails on an input of 200
function task(timeout) {
  return new Promise((resolve, reject) => {
    console.log(`Starting a ${timeout}ms task... `);
    if (timeout == 200) {
      console.log(`Starting a ${timeout}ms task... FAILED. timeout = ${timeout}`);
      reject(`task(${timeout}): FAILED`);
    } else {
      setTimeout(() => {
        let result = timeout * 10;
        output.push(result);
        console.log(`Starting a ${timeout}ms task... done. Result: ${result}`);
        resolve();
      }, timeout);
    }
  });
}

let input = [400, 300, 200, 100, 50];
let output = [];

let p = Promise.resolve(); // initialize
for (let i = 0; i < input.length;   i) {
  let timeout = input[i];
  p = p
    .then(() => {
      return task(timeout);
    });
}

p = p
  .catch(() => {}) // error handled!
  .finally(() => {
    console.log(`Results so far: ${output.join(", ")}`);
  });  

В качестве альтернативы …

  • удаление output.push from task и перемещение его туда, где выполняются задачи
  • упрощение цикла

Вы можете сделать

 function task(timeout) {
  return new Promise((resolve, reject) => {
    console.log(`Starting a ${timeout}ms task... `);
    if (timeout == 200) {
      console.log(`Starting a ${timeout}ms task... FAILED. timeout = ${timeout}`);
      reject(`task(${timeout}): FAILED`);
    } else {
      setTimeout(() => {
        let result = timeout * 10;
        console.log(`Starting a ${timeout}ms task... done. Result: ${result}`);
        resolve(result); // resolve the result
      }, timeout);
    }
  });
}
let input = [400, 300, 200, 100, 50];
let output = [];
               // adding the result to output here vvvvvvvvvvvvvvvvvvvvvvvvv
let p = input.reduce((p, t)=>p.then(() => task(t)).then(r => output.push(r)), Promise.resolve());
p
.catch((e) => {})
.finally(() => console.log(`Results so far: ${output.join(", ")}`))