一部が拒否されても、すべての約束が完了するまで待ちます


405

Promiseネットワーク要求を行っているのセットがあり、そのうちの1つが失敗するとします。

// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]

Promise.all(arr)
  .then(res => console.log('success', res))
  .catch(err => console.log('error', err)) // This is executed   

失敗したかどうかに関係なく、これらがすべて完了するまで待機したいとします。リソースがなくても存続できるリソースにネットワークエラーがある可能性がありますが、取得できる場合は続行する前にそのエラーが必要です。ネットワークの障害を適切に処理したい。

このためのPromises.all余地はないので、promisesライブラリを使用せずにこれを処理するための推奨パターンは何ですか?


拒否されたpromiseの結果の配列で何を返す必要がありますか?
Kuba Wyrostek、2015

9
ES6の約束はそのような方法をサポートしていません(現在のところBluebirdより明らかに遅いです)。さらに、すべてのブラウザーまたはエンジンがまだそれらをサポートしているわけではありません。私は、Bluebirdを使用することを強くお勧めします。Bluebirdは、allSettled自分でロールすることなく、ニーズを満たします。
Dan Pantry、2015

@KubaWyrostek Promise.allがこのような振る舞いをしていない理由をあなたは思います。これはそれがどのように機能するかではありませんが、別の見方は、Promise.allは失敗しない特別なプロミスを返す必要があると言うことです-そして失敗したプロミスを表す引数としてスローされたエラーを取得します。
Nathan Hagen

ダンが共有したものに追加するには、bluebirdが持っているallSettled / settleAllのような機能を「反映」関数を介して利用できます。
user3344977

2
@Coli:うーん、そうは思いません。いずれかの約束Promise.allが拒否されるとすぐに拒否されるため、提案されたイディオムはすべての約束が解決されることを保証しません。
イェルクWミッターク

回答:


309

更新、おそらく組み込みのネイティブを使用したいPromise.allSettled

Promise.allSettled([promise]).then(([result]) => {
   //reach here regardless
   // {status: "fulfilled", value: 33}
});

面白い事実として、以下のこの回答は、その方法を言語に追加する際の先行技術でした。]


もちろん、必要なのはreflect

const reflect = p => p.then(v => ({v, status: "fulfilled" }),
                            e => ({e, status: "rejected" }));

reflect(promise).then((v => {
    console.log(v.status);
});

またはES5の場合:

function reflect(promise){
    return promise.then(function(v){ return {v:v, status: "fulfilled" }},
                        function(e){ return {e:e, status: "rejected" }});
}


reflect(promise).then(function(v){
    console.log(v.status);
});

またはあなたの例では:

var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]

Promise.all(arr.map(reflect)).then(function(results){
    var success = results.filter(x => x.status === "fulfilled");
});

3
これは素晴らしい解決策だと思います。より簡単な構文を含めるように修正できますか?問題の核心は、サブプロミスでエラーを処理したい場合、それらをキャッチしてエラーを返すことです。したがって、例: gist.github.com/nhagen/a1d36b39977822c224b8
Nathan Hagen

3
@NathanHagenを使用すると、何が拒否され何が満たされたかを把握して、再利用可能なオペレーターに問題を抽出できます。
Benjamin Gruenbaum 2015

4
私自身の問題に対応して、次のnpmパッケージを作成しました: github.com/Bucabug/promise-reflect npmjs.com/package/promise-reflect
SamF

2
私はしばらく前にこの問題に遭遇し、このnpmパッケージを作成しました:npmjs.com/package/promise-all-soft-fail
velocity_distance

5
この言葉reflectはコンピュータサイエンスで一般的な言葉ですか?ウィキペディアなどで説明されている場所にリンクしてください。私は一生懸命検索してPromise.all not even first rejectいましたが、「Reflect」を検索することを知りませんでした。ES6は、Promise.reflect「Promise.all but really all」のようなものを持つべきですか?
ノイティダート2018

253

同様の答えですが、おそらくES6の方が慣用的です。

const a = Promise.resolve(1);
const b = Promise.reject(new Error(2));
const c = Promise.resolve(3);

Promise.all([a, b, c].map(p => p.catch(e => e)))
  .then(results => console.log(results)) // 1,Error: 2,3
  .catch(e => console.log(e));


const console = { log: msg => div.innerHTML += msg + "<br>"};
<div id="div"></div>

返される値のタイプ(複数可)に応じて、エラーが頻繁に簡単に十分な(例えば、使用を区別することができundefined、「気にしない」ためtypeof、プレーンな非オブジェクト値のためresult.messageresult.toString().startsWith("Error:")など)


1
@KarlBateman混乱していると思います。.map(p => p.catch(e => e))部品がすべての拒否を解決済みの値に変換するため、ここでは関数の解決または拒否の順序は関係ありません。Promise.all個々の関数が解決または拒否するかどうかに関係なく、すべての処理が完了するまで待機します。それを試してみてください。
ジブ

39
.catch(e => console.log(e));これが失敗することはないので呼び出されません
fregante

4
@ bfred.itそうです。でプロミスチェーンを終了することcatchは一般的に良い方法ですが、私見です。
ジブ2017年

2
@SuhailGuptaエラーeをキャッチし、通常の(成功した)値として返します。p.catch(function(e) { return e; })短いだけと同じ。return暗黙的です。
ジブ2017年

1
@JustinReusnowはすでにコメントでカバーされています。後でコードを追加する場合に備えて、チェーンを終了することは常に良い習慣です。
jib 2017年

71

ベンジャミンの答えはこの問題を解決するための優れた抽象化を提供しますが、私はそれほど抽象化されていない解決策を望んでいました。この問題を解決する明示的な方法は.catch、内部のpromiseを呼び出すだけで、コールバックからエラーを返すことです。

let a = new Promise((res, rej) => res('Resolved!')),
    b = new Promise((res, rej) => rej('Rejected!')),
    c = a.catch(e => { console.log('"a" failed.'); return e; }),
    d = b.catch(e => { console.log('"b" failed.'); return e; });

Promise.all([c, d])
  .then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
  .catch(err => console.log('Catch', err));

Promise.all([a.catch(e => e), b.catch(e => e)])
  .then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
  .catch(err => console.log('Catch', err));

これをさらに一歩進めて、次のような汎用的なキャッチハンドラを作成できます。

const catchHandler = error => ({ payload: error, resolved: false });

その後、あなたはできる

> Promise.all([a, b].map(promise => promise.catch(catchHandler))
    .then(results => console.log(results))
    .catch(() => console.log('Promise.all failed'))
< [ 'Resolved!',  { payload: Promise, resolved: false } ]

これの問題は、キャッチされた値がキャッチされていない値とは異なるインターフェースを持つことになるため、これをクリーンアップするには、次のようにします。

const successHandler = result => ({ payload: result, resolved: true });

だから今これを行うことができます:

> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
    .then(results => console.log(results.filter(result => result.resolved))
    .catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]

次に、それを乾燥状態に保つために、ベンジャミンの答えを得る:

const reflect = promise => promise
  .then(successHandler)
  .catch(catchHander)

今のところ

> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
    .then(results => console.log(results.filter(result => result.resolved))
    .catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]

2番目のソリューションの利点は、その抽象化とDRYです。欠点は、コードが増えることと、一貫性を保つためにすべての約束を反映することを忘れないことです。

私は私のソリューションを明示的かつKISSとして特徴付けますが、実際には堅牢性は低くなります。インターフェースは、promiseが成功したか失敗したかを正確に知ることを保証しません。

たとえば、次のようになります。

const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));

これはに巻き込まれないa.catchので、

> Promise.all([a, b].map(promise => promise.catch(e => e))
    .then(results => console.log(results))
< [ Error, Error ]

どれが致命的で、どれがそうでなかったかを区別する方法はありません。それが重要な場合は、それが実行されたかどうかを追跡し、それを追跡および実行するインターフェースを使用する必要があります(これはreflect)。

エラーを適切に処理したい場合は、エラーを未定義の値として扱うことができます。

> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
    .then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]

私の場合、エラーやどのように失敗したかを知る必要はありません。値があるかどうかだけ気にします。promiseを生成する関数に、特定のエラーのログ記録について心配させます。

const apiMethod = () => fetch()
  .catch(error => {
    console.log(error.message);
    throw error;
  });

このようにして、アプリケーションの残りの部分は、必要に応じてエラーを無視し、必要に応じて未定義の値として扱うことができます。

高レベルの関数が安全に失敗し、依存関係が失敗した理由の詳細について心配しないようにしたいと思います。また、そのトレードオフを行う必要がある場合は、KISSをDRYよりも優先しますreflect。これが、最終的にを使用しないことを選択した理由です。


1
@ベンジャミン@ネイサンの解決策は非常に簡単で、Promisesにとって慣用的だと思います。reflectコードの再利用が改善される一方で、別のレベルの抽象化も確立されます。これまでのところ、ネイサンの回答はあなたの回答と比較して賛成票のほんの一部しか受け取っていないので、これは彼の解決策の問題を示しているのかどうか疑問に思いますが、まだ認識していません。

2
@ LUH3417このソリューションは、エラーを値として扱い、エラーと非エラーを区別しないため、概念的には音が小さくなります。たとえば、promiseの1つがスローされる可能性のある値に正当に解決される場合(これは完全に可能です)、これはかなりひどく壊れます。
Benjamin Gruenbaum

2
@BenjaminGruenbaumだから、たとえば、new Promise((res, rej) => res(new Error('Legitimate error'))から区別できないでしょうnew Promise(((res, rej) => rej(new Error('Illegitimate error'))か?さらに、フィルターすることはできませんx.statusか?この点を回答に追加して、違いがより明確になるようにします
Nathan Hagen

3
これが悪いアイデアである理由は、Promise実装を特定のPromise.all()バリアントでのみ使用される特定のユースケースに結び付けるためです。また、特定のPromiseが拒否されないが、エラーを飲み込みます。実際、このreflect()メソッドを呼び出すことで、「抽象化」を減らし、より明示的にすることができPromiseEvery(promises).then(...)ます。ベンジャミンと比較した上記の回答の複雑さは、このソリューションについて多くを語るべきです。
Neil

33

あり、完成提案バニラJavaScriptで、ネイティブにこれを達成することができます機能について:Promise.allSettled、ステージ4にそれを作った、ES2020にofficializedされては、とに実装されているすべての近代的な環境。これはreflectこの他の回答の関数と非常に似ています。提案ページの例です。以前は、以下を実行する必要がありました。

function reflect(promise) {
  return promise.then(
    (v) => {
      return { status: 'fulfilled', value: v };
    },
    (error) => {
      return { status: 'rejected', reason: error };
    }
  );
}

const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.all(promises.map(reflect));
const successfulPromises = results.filter(p => p.status === 'fulfilled');

Promise.allSettled代わりに使用すると、上記は次と同等になります。

const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const successfulPromises = results.filter(p => p.status === 'fulfilled');

最新の環境を使用しているユーザーは、ライブラリなしでこの方法を使用できます。これらでは、次のスニペットが問題なく実行されるはずです。

Promise.allSettled([
  Promise.resolve('a'),
  Promise.reject('b')
])
  .then(console.log);

出力:

[
  {
    "status": "fulfilled",
    "value": "a"
  },
  {
    "status": "rejected",
    "reason": "b"
  }
]

古いブラウザの場合、仕様に準拠したポリフィルがここにあります


1
それはステージ4で、ES2020に上陸することになっています。
Estus Flask

ノード12でも利用可能:)
Callum M

他の回答がまだ有効である場合でも、これはこの問題を解決する最新の方法であるため、より多くの賛成票を獲得する必要があります。
ジェイコブ

9

私はベンジャミンの答えが本当に好きで、彼がどのように基本的にすべての約束を常に解決しますが、時にはエラーとして結果としての約束に変える方法が好きです。:)
あなたが代替案を探していた場合に備えて、あなたの要求に対する私の試みはここにあります。このメソッドは単にエラーを有効な結果として扱い、Promise.allそれ以外の場合と同様にコード化されています。

Promise.settle = function(promises) {
  var results = [];
  var done = promises.length;

  return new Promise(function(resolve) {
    function tryResolve(i, v) {
      results[i] = v;
      done = done - 1;
      if (done == 0)
        resolve(results);
    }

    for (var i=0; i<promises.length; i++)
      promises[i].then(tryResolve.bind(null, i), tryResolve.bind(null, i));
    if (done == 0)
      resolve(results);
  });
}

これは通常と呼ばれsettleます。ブルーバードにもそれがあります。私はよりよく反映するのが好きですが、これがアレイにこれがある場合の実行可能なソリューションです。
Benjamin Gruenbaum 2015

2
確かに、定住は本当に良い名前になります。:)
Kuba Wyrostek、2015

これは、明示的なプロミス構築アンチパターンによく似ています。このような関数を自分で作成することはできませんが、ライブラリが提供する関数を使用してください(OK、ネイティブES6は少し微妙です)。
Bergi、2015

Promiseコンストラクタを適切に使用してください(そしてそれを避けてくださいvar resolve)?
ベルギ

ベルギ、必要に応じて回答を自由に変更してください。
Kuba Wyrostek、2015

5
var err;
Promise.all([
    promiseOne().catch(function(error) { err = error;}),
    promiseTwo().catch(function(error) { err = error;})
]).then(function() {
    if (err) {
        throw err;
    }
});

Promise.all拒否された約束を飲み込むと約束のすべてが解決したときに、それが返されますので、変数にエラーを格納します。その後、エラーを再スローするか、何でもできます。このようにして、最初の拒否の代わりに最後の拒否を抜け出せると思います。


1
このように配列を作成してを使用するとerr.push(error)、エラーが集計される可能性があるため、すべてのエラーがバブルアップする可能性があります。
ps2goat

4

私は同じ問題を抱えており、次の方法でそれを解決しました:

const fetch = (url) => {
  return node-fetch(url)
    .then(result => result.json())
    .catch((e) => {
      return new Promise((resolve) => setTimeout(() => resolve(fetch(url)), timeout));
    });
};

tasks = [fetch(url1), fetch(url2) ....];

Promise.all(tasks).then(......)

その場合Promise.allすべての約束が入るresolvedか、またはrejected状態になるのを待ちます。

そして、このソリューションにより、私たちはcatchノンブロッキングの方法で「実行を停止」しています。実際、何も停止せず、タイムアウト後に解決されたときにPromise別のものを返す保留状態でを返すだけPromiseです。


しかし、それを実行すると、すべてのpromiseが自由に呼び出されますPromise.all。すべてのプロミスが呼び出されたときにリッスンする方法を探していますが、自分でプロミスを呼び出すわけではありません。ありがとう。
SudoPlz 2018年

@SudoPlzメソッドall()はそれを行い、すべてのPromiseが満たされるか、少なくとも1つが拒否されるのを待ちます。
user1016265

それは本当ですが、待つだけでなく、実際にプロセスを呼び出し/開始/起動します。可能性のない他の場所で約束を実行したい場合は、.allすべてを実行します。
SudoPlz

@SudoPlzこれがあなたの意見を変えることを願っていますjsfiddle.net/d1z1vey5
user1016265

3
私は修正された立場です。これまで、Promiseは誰かが呼び出したとき(別名a thenまたは.all呼び出し)にのみ実行されると思っていましたが、作成時に実行されました。
SudoPlz 2018年

2

これは、Qの方法と一致している必要があります

if(!Promise.allSettled) {
    Promise.allSettled = function (promises) {
        return Promise.all(promises.map(p => Promise.resolve(p).then(v => ({
            state: 'fulfilled',
            value: v,
        }), r => ({
            state: 'rejected',
            reason: r,
        }))));
    };
}

2

ベンジャミン・グリューンバウムの答えはもちろん素晴らしいです。しかし、抽象化のレベルを持つNathan Hagenの視点が曖昧に見えることもわかります。のような短いオブジェクトプロパティを使用しても効果e & vはありませんが、もちろん変更することができます。

JavaScriptには、という標準のErrorオブジェクトがありますError。理想的には、常にこのインスタンスまたは子孫をスローします。利点は、あなたができることinstanceof Error、そして何かがエラーであることを知っていることです。

このアイデアを使用して、これが問題に対する私の見解です。

基本的にエラーをキャッチします。エラーのタイプがErrorでない場合は、Errorオブジェクト内でエラーをラップします。結果の配列には、解決された値、または確認できるErrorオブジェクトが含まれます。

catch内のinstanceofはreject("error")、の代わりにを実行した可能性のある外部ライブラリを使用する場合に使用しますreject(new Error("error"))

もちろん、エラーを解決することを約束することもできますが、その場合は、最後の例が示すように、とにかくエラーとして処理することが理にかなっています。

これを行うもう1つの利点は、配列の破棄が単純に保たれることです。

const [value1, value2] = PromiseAllCatch(promises);
if (!(value1 instanceof Error)) console.log(value1);

の代わりに

const [{v: value1, e: error1}, {v: value2, e: error2}] = Promise.all(reflect..
if (!error1) { console.log(value1); }

!error1チェックはinstanceofよりも簡単であると主張することもできますが、両方を破棄する必要もありv & eます。

function PromiseAllCatch(promises) {
  return Promise.all(promises.map(async m => {
    try {
      return await m;
    } catch(e) {
      if (e instanceof Error) return e;
      return new Error(e);
    }
  }));
}


async function test() {
  const ret = await PromiseAllCatch([
    (async () => "this is fine")(),
    (async () => {throw new Error("oops")})(),
    (async () => "this is ok")(),
    (async () => {throw "Still an error";})(),
    (async () => new Error("resolved Error"))(),
  ]);
  console.log(ret);
  console.log(ret.map(r =>
    r instanceof Error ? "error" : "ok"
    ).join(" : ")); 
}

test();


2

拒否する代わりに、オブジェクトで解決します。あなたはpromiseを実装しているときにあなたはこのようなことをすることができます

const promise = arg => {
  return new Promise((resolve, reject) => {
      setTimeout(() => {
        try{
          if(arg != 2)
            return resolve({success: true, data: arg});
          else
            throw new Error(arg)
        }catch(e){
          return resolve({success: false, error: e, data: arg})
        }
      }, 1000);
  })
}

Promise.all([1,2,3,4,5].map(e => promise(e))).then(d => console.log(d))


1
これは見栄えが良く、エレガントではありませんが機能します
Sunny Tambi

1

私は...以下の申し出若干異なるアプローチを考えて比較するfn_fast_fail()fn_slow_fail()...、後者のような失敗しませんが...あなたは、1つまたは両方のかどうかを確認することができますabのインスタンスであるErrorthrowことError届いて、それをしたい場合catchブロック(例えばif (b instanceof Error) { throw b; })。jsfiddleを参照してください

var p1 = new Promise((resolve, reject) => { 
    setTimeout(() => resolve('p1_delayed_resolvement'), 2000); 
}); 

var p2 = new Promise((resolve, reject) => {
    reject(new Error('p2_immediate_rejection'));
});

var fn_fast_fail = async function () {
    try {
        var [a, b] = await Promise.all([p1, p2]);
        console.log(a); // "p1_delayed_resolvement"
        console.log(b); // "Error: p2_immediate_rejection"
    } catch (err) {
        console.log('ERROR:', err);
    }
}

var fn_slow_fail = async function () {
    try {
        var [a, b] = await Promise.all([
            p1.catch(error => { return error }),
            p2.catch(error => { return error })
        ]);
        console.log(a); // "p1_delayed_resolvement"
        console.log(b); // "Error: p2_immediate_rejection"
    } catch (err) {
        // we don't reach here unless you throw the error from the `try` block
        console.log('ERROR:', err);
    }
}

fn_fast_fail(); // fails immediately
fn_slow_fail(); // waits for delayed promise to resolve

0

これが私の習慣です settledPromiseAll()

const settledPromiseAll = function(promisesArray) {
  var savedError;

  const saveFirstError = function(error) {
    if (!savedError) savedError = error;
  };
  const handleErrors = function(value) {
    return Promise.resolve(value).catch(saveFirstError);
  };
  const allSettled = Promise.all(promisesArray.map(handleErrors));

  return allSettled.then(function(resolvedPromises) {
    if (savedError) throw savedError;
    return resolvedPromises;
  });
};

に比べ Promise.all

  • すべてのpromiseが解決されると、標準のpromiseとまったく同じように機能します。

  • 1つ以上のプロミスが拒否された場合、標準のプロミスとほとんど同じように拒否された最初のプロミスが返されますが、すべてのプロミスが解決/拒否されるのを待ちます。

勇敢なために私たちは変えることができますPromise.all()

(function() {
  var stdAll = Promise.all;

  Promise.all = function(values, wait) {
    if(!wait)
      return stdAll.call(Promise, values);

    return settledPromiseAll(values);
  }
})();

注意深い。他の無関係なJSライブラリを壊したり、JS標準への将来の変更と衝突したりする可能性があるため、通常、組み込みは変更しません。

settledPromiseallは下位互換性がありますPromise.allあり、その機能を拡張しています。

標準を開発している人々-これを新しいPromise標準に含めてみませんか?


0

Promise.all現代的なasync/awaitアプローチを使用して

const promise1 = //...
const promise2 = //...

const data = await Promise.all([promise1, promise2])

const dataFromPromise1 = data[0]
const dataFromPromise2 = data[1]

-1

私はします:

var err = [fetch('index.html').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); }),
fetch('http://does-not-exist').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); })];

Promise.all(err)
.then(function (res) { console.log('success', res) })
.catch(function (err) { console.log('error', err) }) //never executed

-1

同期エグゼキューターnsynjsを介してロジックを順次実行できます。各プロミスで一時停止し、解決/拒否を待機して、解決の結果をdataプロパティに割り当てるか、例外をスローします(try / catchブロックが必要な処理のため)。次に例を示します。

function synchronousCode() {
    function myFetch(url) {
        try {
            return window.fetch(url).data;
        }
        catch (e) {
            return {status: 'failed:'+e};
        };
    };
    var arr=[
        myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"),
        myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js"),
        myFetch("https://ajax.NONEXISTANT123.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js")
    ];
    
    console.log('array is ready:',arr[0].status,arr[1].status,arr[2].status);
};

nsynjs.run(synchronousCode,{},function(){
    console.log('done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>


-1

ES5以降、以下のコードを使用しています。

Promise.wait = function(promiseQueue){
    if( !Array.isArray(promiseQueue) ){
        return Promise.reject('Given parameter is not an array!');
    }

    if( promiseQueue.length === 0 ){
        return Promise.resolve([]);
    }

    return new Promise((resolve, reject) =>{
        let _pQueue=[], _rQueue=[], _readyCount=false;
        promiseQueue.forEach((_promise, idx) =>{
            // Create a status info object
            _rQueue.push({rejected:false, seq:idx, result:null});
            _pQueue.push(Promise.resolve(_promise));
        });

        _pQueue.forEach((_promise, idx)=>{
            let item = _rQueue[idx];
            _promise.then(
                (result)=>{
                    item.resolved = true;
                    item.result = result;
                },
                (error)=>{
                    item.resolved = false;
                    item.result = error;
                }
            ).then(()=>{
                _readyCount++;

                if ( _rQueue.length === _readyCount ) {
                    let result = true;
                    _rQueue.forEach((item)=>{result=result&&item.resolved;});
                    (result?resolve:reject)(_rQueue);
                }
            });
        });
    });
};

使用法の署名はちょうどのようPromise.allです。主な違いは、Promise.waitすべての約束が仕事を終えるのを待つことです。


-1

この質問には多くの答えがあることを知っています。そして、すべてではないにしても、正しいはずです。しかし、これらの答えのロジック/フローを理解するのは非常に困難でした。

そこで、の元の実装を調べて、Promise.all()そのロジックを真似ようとしました。ただし、1つのPromiseが失敗した場合に実行を停止しないことを除きます。

  public promiseExecuteAll(promisesList: Promise<any>[] = []): Promise<{ data: any, isSuccess: boolean }[]>
  {
    let promise: Promise<{ data: any, isSuccess: boolean }[]>;

    if (promisesList.length)
    {
      const result: { data: any, isSuccess: boolean }[] = [];
      let count: number = 0;

      promise = new Promise<{ data: any, isSuccess: boolean }[]>((resolve, reject) =>
      {
        promisesList.forEach((currentPromise: Promise<any>, index: number) =>
        {
          currentPromise.then(
            (data) => // Success
            {
              result[index] = { data, isSuccess: true };
              if (promisesList.length <= ++count) { resolve(result); }
            },
            (data) => // Error
            {
              result[index] = { data, isSuccess: false };
              if (promisesList.length <= ++count) { resolve(result); }
            });
        });
      });
    }
    else
    {
      promise = Promise.resolve([]);
    }

    return promise;
  }

説明:
-入力promisesListをループして、各Promiseを実行します。
-Promiseが解決または拒否されたかどうかに関係なく、Promiseの結果をresultに従って配列に保存しindexます。解決/拒否ステータスも保存します(isSuccess)。
-すべてのPromiseが完了したら、他のすべての結果とともに1つのPromiseを返します。

使用例:

const p1 = Promise.resolve("OK");
const p2 = Promise.reject(new Error(":-("));
const p3 = Promise.resolve(1000);

promiseExecuteAll([p1, p2, p3]).then((data) => {
  data.forEach(value => console.log(`${ value.isSuccess ? 'Resolve' : 'Reject' } >> ${ value.data }`));
});

/* Output: 
Resolve >> OK
Reject >> :-(
Resolve >> 1000
*/

2
Promise.all自分で再実装しようとしないでください。うまくいかないことが多すぎます。たとえば、バージョンは空の入力を処理しません。
ベルギ

-4

使用しているプロミスライブラリはわかりませんが、ほとんどがallSettledのようなものですます。

編集:OK、外部ライブラリなしでプレーンES6を使用したいので、そのような方法はありません。

つまり、すべてのプロミスが解決したらすぐに、プロミスを手動でループし、新しい結合されたプロミスを解決する必要があります。


明確にするために質問を編集しました。ES6にはPromiseが付属しているため、基本的な機能だと思うものに別のライブラリを使用することは避けたいです。答えを得るには、promiseライブラリの1つからソースをコピーするのが良いでしょう。
Nathan Hagen、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.