Array.mapで非同期待機を使用する


170

次のコードがあるとします:

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

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

次のエラーが発生します。

TS2322:タイプ 'Promise <number> []'はタイプ 'number []'に割り当てることができません。タイプ 'Promise <number>はタイプ' number 'に割り当てることができません。

どうすれば修正できますか?一緒に作っasync awaitArray.map一緒に作業するにはどうすればよいですか?


6
同期操作を非同期操作にしようとするのはなぜですか? arr.map()同期であり、promiseを返しません。
jfriend00 2016年

2
のような関数に非同期操作を送信することはできませんmap。これは、同期操作を期待し、機能することを期待しています。
異教の猿

1
@ jfriend00内部関数に多くのawaitステートメントがあります。これは実際には長い関数ですが、読みやすくするために単純化しました。非同期でなければならない理由を明確にするために、待機コールを追加しました。
Alon

配列を返すものではなく、promiseを返すものを待つ必要があります。
jfriend00 2016年

2
理解しておくと便利なことの1つは、関数をとしてマークするたびにasync、その関数がpromiseを返すようにすることです。したがって、もちろん、非同期のマップはpromiseの配列を返します:)
Anthony Manning-Franklin

回答:


380

ここでの問題はawait、約束ではなく一連の約束をしようとしていることです。これはあなたが期待することをしません。

渡されたオブジェクトがawaitPromiseでない場合、await値を解決しようとするのではなく、ただちにそのまま値を返します。したがって、awaitここではPromiseの代わりに(Promiseオブジェクトの)配列を渡したので、awaitから返される値は、単にその配列であり、型Promise<number>[]です。

ここで行う必要があるのはPromise.all、によって返される配列を呼び出してmapから、それを単一のPromiseに変換してから、それを使用awaitすることです。

MDN docsにPromise.allよると:

このPromise.all(iterable)メソッドは、反復可能引数のすべてのプロミスが解決されたときに解決するプロミスを返すか、拒否された最初に渡されたプロミスの理由で拒否します。

だからあなたの場合:

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

これにより、ここで発生している特定のエラーが解決されます。


1
何をする:コロンの意味は?
Danielはモニカを

12
@DanielPendergast TypeScriptの型注釈用です。
Ajedi32 2017年

非同期マップ関数内での呼び出しcallAsynchronousOperation(item);となしの呼び出しの違いは何awaitですか?
nerdizzle

@nerdizzle別の質問の良い候補のように思えます。基本的にはawait、関数は非同期操作が完了する(または失敗する)まで待機してから続行します。そうでない場合は、待機せずにただちに続行します。
Ajedi32

応答には@ Ajedi32 thx。しかし、非同期マップでの待機がないと、関数のre結果を待つことができなくなりますか?
nerdizzle

16

ネイティブのプロミスではなくブルーバードを使用している場合は、別の解決策があります。

また、array.mapとPromise.allを組み合わせて、Promise.map()を試すこともできます。

あなたの場合:

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

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

2
それは異なります-すべての操作を並行して実行するのではなく、順番に実行します。
Andrey Tserkus 2017年

5
@AndreyTserkus Promise.mapSeriesまたは順次Promise.eachPromise.mapそれらをすべて一度に開始します。
Kiechlus

1
@AndreyTserkus concurrencyオプションを提供することで、すべてまたは一部の操作を並行して実行できます。

11
それがバニラJSではないことは言及する価値があります。
ミカル

@Michalええ、それはSyntaxErrorです
CS QGB


2

上記のようにPromise.allを使用することをお勧めしますが、そのアプローチを回避したい場合は、forまたはその他のループを実行できます。

const arr = [1,2,3,4,5];
let resultingArr = [];
for (let i in arr){
  await callAsynchronousOperation(i);
  resultingArr.push(i + 1)
}

6
Promise.allは、配列の各要素に対して非同期になります。これは同期になります。次の要素を開始するには、1つの要素が完了するまで待機する必要があります。
サンティアゴメンドーサラミレス

このアプローチを試す場合、for..ofは配列コンテンツを反復する適切な方法であるのに対し、for..inはインデックスを反復することに注意してください。
ラルフォイド

2

配列のすべての要素を非同期で処理し、順序を維持するための以下のソリューション:

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(n => calc(n));
  const results = await Promise.all(unresolvedPromises);
};

asyncFunc();

また、コードペン

Promise.allのみを「待つ」ことに注意してください。「待機」なしでcalcを複数回呼び出し、未解決の約束の配列をすぐに収集します。次に、Promise.allはそれらすべての解決を待機し、解決された値を含む配列を順番に返します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.