デフォルトでは、Promise.All([])関数は、各Promiseの結果を含む数値ベースのインデックス配列を返します。
var promises = [];
promises.Push(myFuncAsync1()); //returns 1
promises.Push(myFuncAsync1()); //returns 2
Promise.all(promises).then((results)=>{
//results = [0,1]
}
Promise.all()を使用して結果の名前付きインデックスを返すための最良のVanillaの方法は何ですか?
Mapを試してみましたが、次のように配列で結果が返されます:[key1, value1, key2, value2]
更新:
私の質問は不明確に思えますが、これが順序ベースのインデックスが好きではない理由です:
results[42]
(以下のjibの回答で修正できます)var promises = [];
if(...)
promises.Push(...);
else{
[...].forEach(... => {
if(...)
promises.Push(...);
else
[...].forEach(... => {
promises.Push(...);
});
});
}
Promise.all(promises).then((resultsArr)=>{
/*Here i am basically fucked without clear named results
that dont rely on promises' ordering in the array */
});
こういうことですか?
var promises = [];
promises.Push(myFuncAsync1().then(r => {name : "func1", result : r}));
promises.Push(myFuncAsync1().then(r => {name : "func2", result : r}));
Promise.all(promises).then(results => {
var lookup = results.reduce((prev, curr) => {
prev[curr.name] = curr.result;
return prev;
}, {});
var firstResult = lookup["func1"];
var secondResult = lookup["func2"];
}
ES6はdestructuringをサポートしているため、結果に名前を付けるだけの場合は、次のように記述できます。
var myFuncAsync1 = () => Promise.resolve(1);
var myFuncAsync2 = () => Promise.resolve(2);
Promise.all([myFuncAsync1(), myFuncAsync2()])
.then(([result1, result2]) => console.log(result1 +" and "+ result2)) //1 and 2
.catch(e => console.error(e));
Firefoxで動作し、Chromeになりました。
@kragovipの回答に関して、それを避けたい理由は次のとおりです。
「...すべてのネットワークとI/O呼び出しを待つことに慣れるのは本当に簡単です。
ただし、awaitキーワードはその後のすべてのコードの実行を停止するため、連続して複数回使用する場合は注意が必要です。 (同期コードの場合とまったく同じです)」
悪い例(フォローしないでください)
async function processData() {
const data1 = await downloadFromService1();
const data2 = await downloadFromService2();
const data3 = await downloadFromService3();
...
}
「他のリクエストはその結果に依存しないため、最初のリクエストの完了を待つ必要もまったくありません。
リクエストを並行して送信し、すべてが同時に終了するのを待ちたいと思います。ここに、非同期イベント駆動型プログラミングの力があります。
これを修正するには、Promise.all()メソッドを使用できます。 Promiseを変数への非同期関数呼び出しから保存し、それらを配列に結合して、一度にすべて待機します。」
代わりに
async function processData() {
const promise1 = downloadFromService1();
const promise2 = downloadFromService2();
const promise3 = downloadFromService3();
const allResults = await Promise.all([promise1, promise2, promise3]);
結果オブジェクトを変更したくない場合は、各エントリに名前を割り当てて後でアクセスできるようにするヘルパー関数を次に示します。
const allNamed = (nameToPromise) => {
const entries = Object.entries(nameToPromise);
return Promise.all(entries.map(e => e[1]))
.then(results => {
const nameToResult = {};
for (let i = 0; i < results.length; ++i) {
const name = entries[i][0];
nameToResult[name] = results[i];
}
return nameToResult;
});
};
使用法:
var lookup = await allNamed({
rootStatus: fetch('https://stackoverflow.com/').then(rs => rs.status),
badRouteStatus: fetch('https://stackoverflow.com/badRoute').then(rs => rs.status),
});
var firstResult = lookup.rootStatus; // = 200
var secondResult = lookup.badRouteStatus; // = 404
TypeScriptを使用している場合は、 keyof
構文を使用して入力キーと結果の関係を指定することもできます。
type ThenArg<T> = T extends PromiseLike<infer U> ? U : T;
export const allNamed = <
T extends Record<string, Promise<any>>,
TResolved extends {[P in keyof T]: ThenArg<T[P]>}
>(nameToPromise: T): Promise<TResolved> => {
const entries = Object.entries(nameToPromise);
return Promise.all(entries.map(e => e[1]))
.then(results => {
const nameToResult: TResolved = <any>{};
for (let i = 0; i < results.length; ++i) {
const name: keyof T = entries[i][0];
nameToResult[name] = results[i];
}
return nameToResult;
});
};