Чи є анти-шаблоном використання async / await всередині нового конструктора Promise ()?


93

Я використовую async.eachLimitфункцію для контролю максимальної кількості операцій за раз.

const { eachLimit } = require("async");

function myFunction() {
 return new Promise(async (resolve, reject) => {
   eachLimit((await getAsyncArray), 500, (item, callback) => {
     // do other things that use native promises.
   }, (error) => {
     if (error) return reject(error);
     // resolve here passing the next value.
   });
 });
}

Як бачите, я не можу оголосити myFunctionфункцію асинхронною, оскільки не маю доступу до значення всередині другого зворотного виклику eachLimitфункції.


"Як бачите, я не можу оголосити myFunction асинхронним" --- чи можете ви детальніше розповісти?
zerkms

1
О, добре ... вибачте. Мені потрібен конструктор, оскільки мені потрібен async.eachLimit, щоб одночасно уникати більше 500 асинхронних операцій. Я завантажую та витягаю дані з текстових файлів, і я хочу уникати великих асинхронних операцій, після вилучення даних я повинен повернути Promise з даними, і я не зможу повернути його із зворотного виклику async.eachLimit .

1. Навіщо потрібно очікування? Async - це вже механізм керування потоком. 2. Якщо ви хочете використовувати async.js з обіцянками всередині node.js, подивіться на async-q
slebetman

Щоб уникнути пекла зворотного дзвінка, і якщо щось кине, зовнішня обіцянка схопить.

Відповіді:


81

Ви ефективно використовуєте обіцянки всередині функції виконавця конструктора обіцянок, тому це анти-шаблон Promise constructor .

Ваш код є хорошим прикладом основного ризику: не безпечно поширювати всі помилки. Прочитайте, чому там .

Крім того, використання async/ awaitможе зробити ті самі пастки ще більш дивовижними. Порівняйте:

let p = new Promise(resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.

з наївним (неправильним) asyncеквівалентом:

let p = new Promise(async resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!

Шукайте останню на веб-консолі веб-переглядача.

Перший працює, оскільки будь-яке негайне виняток у функції виконавця конструктора Promise зручно відкидає нещодавно побудовану обіцянку (але всередині будь-якого .thenви самі).

Другий не працює, оскільки будь-який негайний виняток у asyncфункції відхиляє неявне обіцянку, повернене самою asyncфункцією .

Оскільки повертане значення функції-виконавця обіцянки не використовується, це погана новина!

Ваш код

Немає жодної причини, яку ви не можете визначити myFunctionяк async:

async function myFunction() {
  let array = await getAsyncArray();
  return new Promise((resolve, reject) => {
    eachLimit(array, 500, (item, callback) => {
      // do other things that use native promises.
    }, error => {
      if (error) return reject(error);
      // resolve here passing the next value.
    });
  });
}

Хоча навіщо використовувати застарілі бібліотеки контролю паралельності, коли вони є await?


12
Вам не потрібно return await: return new Promiseдостатньо.
одинокий день

2
Я офіційно схвалюю цю відповідь, я б сказав точно те саме :-)
Бергі,

1
@celoxxx Погляньте тут . Ви справді ніколи не повинні використовувати async.js з обіцянками
Бергі

1
@celoxxx Просто скиньте типи, і він стане простим js. Ви не повинні використовувати async.js, оскільки різні інтерфейси - зворотні виклики у стилі вузла проти обіцянок - спричиняють занадто велике тертя та призводять до непотрібного складного та схильного до помилок коду.
Бергі

1
Я згоден з вами ... Але цей код застарілий, і я рефакторингую, щоб використовувати події + async.js (поки що для контролю межі асинхронізації. Якщо ви знаєте кращий спосіб, будь ласка, скажіть).

16

Я погоджуюсь з наведеними вище відповідями, і все-таки іноді буває акуратніше мати асинхронізацію всередині своєї обіцянки, особливо якщо ви хочете об’єднати кілька операцій, що повертають обіцянки, і уникати then().then()пекла. Я б розглянув можливість використовувати щось подібне у цій ситуації:

const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)

let p = new Promise((resolve, reject) => {
  (async () => {
    try {
      const op1 = await operation1;
      const op2 = await operation2;

      if (op2 == null) {
         throw new Error('Validation error');
      }

      const res = op1 + op2;
      const result = await publishResult(res);
      resolve(result)
    } catch (err) {
      reject(err)
    }
  })()
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e));
  1. Функція, передана Promiseконструктору, не є асинхронною, тому лінтери не відображають помилок.
  2. Усі функції асинхронізації можна викликати в послідовному порядку, використовуючи await.
  3. Спеціальні помилки можуть бути додані для перевірки результатів асинхронних операцій
  4. Помилка врешті-решт вдало виявляється.

Однак недоліком є ​​те, що ви повинні пам’ятати, як його класти try/catchта прикріплювати reject.


4
static getPosts(){
    return new Promise( (resolve, reject) =>{
        try {
            const res =  axios.get(url);
            const data = res.data;
            resolve(
                data.map(post => ({
                    ...post,
                    createdAt: new Date(post.createdAt)
                }))
            )
        } catch (err) {
            reject(err);                
        }
    })
}

видаліть await, і async вирішить цю проблему. оскільки ви застосували об’єкт Promise, цього досить.


Отже, у вашому прикладі axios.get(url)функціонуватиме так, ніби його називали як await axios.get(url)?
PrestonDocks,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.