javascript - Är det ett anti-mönster att använda async/await inuti en ny Promise()-konstruktor?


Jag använder async.eachLimit-funktionen för att kontrollera det maximala antalet operationer samtidigt.

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

Som du kan se kan jag inte deklarera funktionen myFunction som async eftersom jag inte har tillgång till värdet inuti den andra callback-funktionen i eachLimit-funktionen.

Svar #1

Du använder effektivt löften inuti löftet-konstruktörens exekveringsfunktion, så detta är löftet-konstruktörens antipattern.

Din kod är ett gott exempel på den största risken: att inte sprida alla fel på ett säkert sätt. Läs varför där.

Dessutom kan användningen av async/await göra samma fällor ännu mer överraskande. Jämför:

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

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

med en naiv (felaktig) async-motsvarighet:

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

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

Titta i webbkonsolen i din webbläsare efter den sista.

Den första fungerar eftersom varje omedelbar undantag i en Promise-constructors exekveringsfunktion bekvämt avslår det nykonstruerade löftet (men inom någon .then är du på egen hand).

Den andra fungerar inte eftersom vilken omedelbar exception som helst i en async-funktion avvisar den implicita promise som returneras av async-funktionen själv.

Eftersom returvärdet från en promise-konstruktörs exekveringsfunktion inte används, är det dåliga nyheter!

Din kod

Det finns ingen anledning till att du inte kan definiera myFunction som 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.
    });
  });
}

Men varför använda föråldrade bibliotek för samtidighetskontroll när du har await?

Svar #2

Baserat på feedbacken från @Bergi vill jag lyfta fram att jag håller med de svar som ges ovan och skulle hellre välja en annan metod.

Men om du behöver ha async inuti ditt löfte, skulle jag överväga att använda något liknande detta:

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. Funktionen som skickas till Promise-konstruktören är inte asynkron, så linter visar inga fel.
  2. Alla asynkrona funktioner kan anropas i sekventiell ordning med await.
  3. Anpassade fel kan läggas till för att validera resultaten av asynkrona operationer
  4. Felet fångas upp snyggt till slut.

En nackdel dock är att du måste komma ihåg att använda try/catch och koppla det till reject.

Svar #3

ATT TRO PÅ ANTI-MÖNSTER ÄR ETT ANTI-MÖNSTER

Undantag inom en async promise-callback kan enkelt fångas.

(async () => {
    try {
        await new Promise (async (FULFILL, BREAK) => {
            try {
                throw null;
            }
            catch (BALL) {
                BREAK (BALL);
            }
        });
    }
    catch (BALL) {
        console.log ("(A) BALL CAUGHT", BALL);
        throw BALL;
    }
}) ().
catch (BALL => {
    console.log ("(B) BALL CAUGHT", BALL);
});

eller ännu enklare,

(async () => {
    await new Promise (async (FULFILL, BREAK) => {
        try {
            throw null;
        }
        catch (BALL) {
            BREAK (BALL);
        }
    });
}) ().
catch (BALL => {
    console.log ("(B) BALL CAUGHT", BALL);
});

Relaterade artiklar