javascript - Folosește async await cu Array.map


Având următorul cod:

var arr = [1,2,3,4,5];

var results: number[] = await arr.map(async (item): Promise<number> => {
        await callAsynchronousOperation(item);
        return item + 1;
    });

care produce următoarea eroare:

TS2322: Tipul 'Promise[]' nu poate fi atribuit tipului 'number[]'. Tipul 'Promise' nu poate fi atribuit tipului 'number'.

Cum pot să repar asta? Cum pot să fac ca async await și Array.map să funcționeze împreună?

Răspuns #1

Problema aici este că încerci să await un array de promisiuni în loc de o Promisiune. Aceasta nu face ceea ce te aștepți.

Când obiectul transmis la await nu este un Promise, await pur și simplu returnează valoarea așa cum este imediat, în loc să încerce să o rezolve. Deci, deoarece ai transmis await un array (de obiecte Promise) aici în loc de un Promise, valoarea returnată de await este pur și simplu acel array, care este de tip Promise<number>[].

Ceea ce probabil vrei să faci este să apelezi Promise.all pe array-ul returnat de map pentru a-l converti într-un singur Promise înainte de a-l await.

Conform documentației MDN pentru Promise.all:

Metoda Promise.all(iterable) returnează o promisiune care se rezolvă atunci când toate promisiunile din argumentul iterable s-au rezolvat, sau se respinge cu motivul primei promisiuni care s-a respins.

Deci, în cazul tău:

var arr = [1, 2, 3, 4, 5];

var results: number[] = await Promise.all(arr.map(async (item): Promise<number> => {
    await callAsynchronousOperation(item);
    return item + 1;
}));

Acest lucru va rezolva eroarea specifică cu care vă confruntați aici.

În funcție de ceea ce încerci exact să faci, s-ar putea să iei în considerare și utilizarea Promise.allSettled, Promise.any sau Promise.race în loc de Promise.all, deși în majoritatea situațiilor (aproape sigur inclusiv în aceasta) Promise.all va fi cea pe care o vei dori.

Răspunsul #2

Aceasta este cea mai simplă cale de a o face.

await Promise.all(
    arr.map(async (element) => {
        ....
    })
)

Răspunsul #3

Soluția de mai jos pentru a folosi corect async await și Array.map împreună. Procesează toate elementele array-ului în paralel, asincron ȘI păstrează ordinea:

const arr = [1, 2, 3, 4, 5, 6, 7, 8];

const randomDelay = () => new Promise(resolve => setTimeout(resolve, Math.random() * 1000));

const calc = async n => {
  await randomDelay();
  return n * 2;
};

const asyncFunc = async() => {
  const unresolvedPromises = arr.map(calc);
  const results = await Promise.all(unresolvedPromises);
  document.write(results);
};

document.write('calculating...');
asyncFunc();

De asemenea, CodePen.

Observați că noi doar „așteptăm” pentru Promise.all. Apelăm calc fără „așteptare” de mai multe ori și colectăm imediat un array de promisiuni nefinalizate. Apoi Promise.all așteaptă rezolvarea tuturor și returnează un array cu valorile rezolvate în ordine.

Răspunsul #4

Există o altă soluție pentru asta dacă nu folosiți Promises native, ci Bluebird.

Ai putea să încerci și folosirea Promise.map(), combinând array.map și Promise.all

În cazul tău:

  var arr = [1,2,3,4,5];

  var results: number[] = await Promise.map(arr, async (item): Promise<number> => {
    await callAsynchronousOperation(item);
    return item + 1;
  });

Răspunsul #5

Dacă faci map la un array de Promises, poți apoi să le rezolvi pe toate într-un array de numere. Vezi Promise.all.

Răspuns #6

Poți folosi:

for await (let resolvedPromise of arrayOfPromises) {
  console.log(resolvedPromise)
}

https://developer.mozilla.org/ro/docs/Web/JavaScript/Reference/Statements/for-await...of

Dacă doriți să folosiți Promise.all() în schimb, puteți opta pentru Promise.allSettled(), astfel încât să aveți un control mai bun asupra promisiunilor respinse.

https://developer.mozilla.org/ro/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled