Aguarde até que todas as promessas sejam concluídas, mesmo que algumas tenham sido rejeitadas.

405

Digamos que eu tenha um conjunto de Promises que esteja fazendo solicitações de rede, das quais uma falhará:

// 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   

Digamos que eu queira esperar até que tudo isso termine, independentemente de uma ter falhado. Pode haver um erro de rede para um recurso sem o qual posso viver, mas que, se conseguir, desejo antes de prosseguir. Eu quero lidar com falhas de rede normalmente.

Como Promises.allnão deixa espaço para isso, qual é o padrão recomendado para lidar com isso, sem usar uma biblioteca de promessas?

Nathan Hagen
fonte
O que deve ser retornado na matriz resultante para promessas que foram rejeitadas?
Kuby Wyrostek
9
O ES6 promete não apoiar esse método (e atualmente é aparentemente mais lento que o Bluebird ). Além disso, nem todos os navegadores ou mecanismos os suportam ainda. Eu recomendaria fortemente o uso do Bluebird, que vem com o allSettledque satisfaz sua necessidade sem que você precise rolar a sua própria.
Dan Pantry
@KubaWyrostek Acho que você mencionou o motivo do Promise.all não ter esse comportamento, o que acho que faz sentido. Não é assim que funciona, mas uma visão alternativa seria dizer Promise.all deve retornar uma promessa especial que nunca falha - e você receberia o erro que foi lançado como o argumento que representa a promessa que falhou.
Nathan Hagen
Para adicionar ao que Dan compartilhou, a funcionalidade allSettled / settleAll que o bluebird possui pode ser utilizada através da função "refletir".
user3344977
2
@Coli: Hmm, acho que não. Promise.allrejeitará assim que qualquer promessa rejeitar, para que o idioma proposto não garanta que todas as promessas sejam cumpridas.
Jörg W Mittag 28/10

Respostas:

310

Atualização, você provavelmente deseja usar o nativo interno Promise.allSettled:

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

Como um fato interessante, esta resposta abaixo foi anterior ao adicionar esse método ao idioma:]


Claro, você só precisa de reflect:

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

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

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

Ou no seu exemplo:

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");
});
Benjamin Gruenbaum
fonte
3
Eu acho que essa é uma ótima solução. Você pode alterá-lo para incluir uma sintaxe mais simples? O cerne da questão é que, se você deseja lidar com erros nas sub promessas, você deve pegá-los e retornar o erro. Assim, por exemplo: gist.github.com/nhagen/a1d36b39977822c224b8
Nathan Hagen
3
@NathanHagen permite que você descubra o que foi rejeitado e o que atendeu e extrai o problema para um operador reutilizável.
Benjamin Gruenbaum
4
Em resposta à minha própria emissão eu criei o seguinte pacote de npm: github.com/Bucabug/promise-reflect npmjs.com/package/promise-reflect
SAMF
2
Corri para este problema há um tempo atrás e eu criei este pacote npm para ele: npmjs.com/package/promise-all-soft-fail
velocity_distance
5
A palavra reflecté comum na ciência da computação? Você pode fazer um link para onde isso é explicado, como na wikipedia ou algo assim. Eu estava procurando muito, Promise.all not even first rejectmas não sabia pesquisar "Reflect". O ES6 deve ter um Promise.reflectque é como "Promise.all, mas realmente todos"?
Noitidart 19/09/19
253

Resposta semelhante, mas mais idiomática para o ES6, talvez:

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>

Dependendo do (s) tipo (s) de valores retornados, os erros geralmente podem ser distinguidos com bastante facilidade (por exemplo, use undefinedpara "não me importo", typeofpara valores simples que não sejam objetos result.message, result.toString().startsWith("Error:")etc.)

jib
fonte
11
@KarlBateman Eu acho que você está confuso. As funções de ordem resolvidas ou rejeitadas não importam aqui, pois a .map(p => p.catch(e => e))peça transforma todas as rejeições em valores resolvidos, portantoPromise.all ainda espera que tudo termine, se as funções individuais resolverem ou rejeitarem, independentemente do tempo que levarem. Tente.
lança
39
.catch(e => console.log(e));nunca é chamado, porque isso nunca falha
fregante
4
@ bfred.it Isso está correto. Embora o encerramento de cadeias de promessas catchseja geralmente uma boa prática IMHO .
lança
2
@SuhailGupta Ele pega o erro ee o retorna como um valor regular (sucesso). O mesmo que p.catch(function(e) { return e; })apenas mais curto. returnestá implícito.
patíbulo
11
@JustinReusnow já está coberto de comentários. Sempre é uma boa prática encerrar cadeias caso você adicione código posteriormente.
patíbulo
71

A resposta de Benjamin oferece uma grande abstração para resolver esse problema, mas eu esperava uma solução menos abstrata. A maneira explícita de resolver esse problema é simplesmente invocar .catchas promessas internas e retornar o erro do retorno de chamada.

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

Dando um passo adiante, você pode escrever um manipulador de captura genérico parecido com este:

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

então você pode fazer

> 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 } ]

O problema é que os valores capturados terão uma interface diferente dos valores não capturados; portanto, para limpar isso, você pode fazer algo como:

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

Então agora você pode fazer isso:

> 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!' ]

Então, para mantê-lo seco, você obtém a resposta de Benjamin:

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

onde agora parece

> 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!' ]

Os benefícios da segunda solução são que ela é abstraída e SECA. A desvantagem é que você tem mais código e precisa se lembrar de refletir todas as suas promessas para tornar as coisas consistentes.

Eu caracterizaria minha solução como explícita e KISS, mas de fato menos robusta. A interface não garante que você saiba exatamente se a promessa foi bem-sucedida ou não.

Por exemplo, você pode ter isso:

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

Isso não será pego por a.catch, então

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

Não há como saber qual foi fatal e qual não foi. Se isso é importante, convém aplicar e fazer uma interface que rastreie se foi bem-sucedida ou não (o quereflect faz).

Se você deseja apenas manipular os erros normalmente, pode apenas tratar os erros como valores indefinidos:

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

No meu caso, não preciso saber o erro ou como ele falhou - apenas me importo se tenho o valor ou não. Vou deixar a função que gera a promessa se preocupar em registrar o erro específico.

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

Dessa forma, o restante do aplicativo pode ignorar seu erro, se desejar, e tratá-lo como um valor indefinido, se desejar.

Quero que minhas funções de alto nível falhem com segurança e não me preocupo com os detalhes de por que suas dependências falharam, e também prefiro o KISS ao DRY quando precisar fazer essa troca - e foi por isso que optei por não usar reflect.

Nathan Hagen
fonte
11
@ Benjamin, acho que a solução do @ Nathan é muito direta e idiomática para Promises. Enquanto você reflectmelhora a reutilização do código, ele também estabelece outro nível de abstração. Como a resposta de Nathan até agora recebeu apenas uma fração de votos positivos em relação à sua, pergunto-me se isso é uma indicação de um problema com a solução dele, que ainda não reconheci.
2
@ LUH3417 esta solução é conceitualmente menos sólida, pois trata os erros como valores e não separa os erros dos não-erros. Por exemplo, se uma das promessas se resolver legitimamente para um valor que pode ser lançado (o que é inteiramente possível), isso quebra muito.
Benjamin Gruenbaum
2
@BenjaminGruenbaum Então, por exemplo, new Promise((res, rej) => res(new Error('Legitimate error'))não seria distinguível new Promise(((res, rej) => rej(new Error('Illegitimate error'))? Ou ainda, você não seria capaz de filtrar por x.status? Vou acrescentar este ponto à minha resposta para que a diferença seja mais clara
Nathan Hagen
3
A razão pela qual essa é uma péssima idéia é porque vincula a implementação do Promise a um caso de uso específico e só é usado em uma Promise.all()variante específica ; também fica a cargo do consumidor do Promise saber que uma promessa específica não será rejeitada, mas será engolir é erros. De fato, o reflect()método poderia ser menos "abstrato" e mais explícito chamando-o PromiseEvery(promises).then(...). A complexidade da resposta acima, em comparação com a de Benjamin, deve dizer muito sobre essa solução.
Neil
33

Existe uma proposta finalizada para uma função que pode realizar isso de forma nativa, em Javascript vanilla Promise.allSettled:, que chegou ao estágio 4, é oficializado no ES2020 e é implementado em todos os ambientes modernos . É muito semelhante à reflectfunção nesta outra resposta . Aqui está um exemplo, na página da proposta. Antes, você teria que fazer:

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

Usando em Promise.allSettledvez disso, o acima será equivalente a:

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

Aqueles que usam ambientes modernos poderão usar esse método sem nenhuma biblioteca . Nesses, o seguinte snippet deve ser executado sem problemas:

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

Resultado:

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

Para navegadores mais antigos, há um polyfill compatível com especificações aqui .

CertainPerformance
fonte
11
É o estágio 4 e deve pousar no ES2020.
Estus Flask
Também disponível no nó 12 :)
Callum M
Mesmo que as outras respostas ainda sejam válidas, essa deve receber mais votos, pois é a maneira mais atual de resolver esse problema.
Jacob
9

Eu realmente gosto da resposta de Benjamin, e como ele basicamente transforma todas as promessas em sempre resolver, mas às vezes com erro como resultado. :)
Aqui está minha tentativa de sua solicitação, caso você esteja procurando alternativas. Esse método simplesmente trata os erros como resultados válidos e é codificado de maneira semelhante 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);
  });
}
Kuba Wyrostek
fonte
Isso geralmente é chamado settle. Temos isso também em bluebird, eu gosto de refletir melhor, mas esta é uma solução viável para quando você tiver isso em uma matriz.
Benjamin Gruenbaum
2
OK, Settle será um nome melhor, de fato. :)
Kuba Wyrostek
Isso se parece muito com o antipadrão de construção de promessa explícita. Deve-se observar que você nunca deve escrever uma função dessas, mas use a que sua biblioteca fornece (OK, o ES6 nativo é um pouco escasso).
Bergi 16/07/2015
Você poderia usar o Promiseconstrutor corretamente (e evitar isso var resolve)?
Bergi 16/07/2015
Bergi, sinta-se à vontade para alterar a resposta da forma que considerar necessária.
Kuby Wyrostek
5
var err;
Promise.all([
    promiseOne().catch(function(error) { err = error;}),
    promiseTwo().catch(function(error) { err = error;})
]).then(function() {
    if (err) {
        throw err;
    }
});

Ele Promise.allengolirá qualquer promessa rejeitada e armazenará o erro em uma variável; portanto, ele retornará quando todas as promessas forem resolvidas. Em seguida, você pode refazer o erro ou fazer o que for. Dessa forma, acho que você obteria a última rejeição em vez da primeira.

martin770
fonte
11
Parece que isso poderia agregar err.push(error)erros, transformando-o em uma matriz e usando-o , para que todos os erros pudessem ocorrer.
Ps2goat 06/04
4

Eu tive o mesmo problema e resolvi-o da seguinte maneira:

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(......)

Nesse caso Promise.all, aguardará que toda Promessa entre resolvedourejected declarem.

E, com esta solução, estamos "interrompendo a catchexecução" de maneira não-bloqueadora. Na verdade, não estamos parando nada, apenas retornamos o Promiseestado pendente que retorna outro Promisequando é resolvido após o tempo limite.

user1016265
fonte
Mas isso invoca todas as promessas à vontade quando você corre Promise.all. Estou procurando uma maneira de ouvir quando todas as promessas foram invocadas, mas não as invoco eu mesmo. Obrigado.
SudoPlz
@SudoPlz o método all()faz isso, aguarda o cumprimento de todas as promessas ou a rejeição de pelo menos uma delas.
precisa saber é o seguinte
isso é verdade, mas não basta esperar, na verdade, ele invoca / inicia / inicia o processo. Se você desejava lançar as promessas em outro lugar que não seria possível, porque .allaciona tudo.
SudoPlz
@SudoPlz espero que isso mude sua opinião jsfiddle.net/d1z1vey5
user1016265 4/18/18
3
Eu estou corrigido. Até agora, eu pensava que as promessas só eram executadas quando alguém as chama (também chamadas de chamada thenou .allchamada), mas são executadas quando criadas.
SudoPlz
2

Isso deve ser consistente com o modo como Q faz :

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,
        }))));
    };
}
mpen
fonte
2

A resposta de Benjamin Gruenbaum é obviamente ótima. Mas também posso ver se o ponto de vista de Nathan Hagen com o nível de abstração parece vago. Ter propriedades curtas de objetos comoe & v também não ajuda, mas é claro que isso pode ser alterado.

Em Javascript, há um objeto Error padrão, chamado Error,. Idealmente, você sempre lança uma instância / descendente disso. A vantagem é que você pode fazerinstanceof Error e sabe que algo é um erro.

Então, usando essa idéia, aqui está minha opinião sobre o problema.

Capture o erro basicamente, se o erro não for do tipo Error, envolva o erro dentro de um objeto Error. A matriz resultante terá valores resolvidos ou objetos de erro que você pode verificar.

A instância dentro da captura, é para o caso de você usar alguma biblioteca externa que talvez tenha usado reject("error"), em vez de reject(new Error("error")).

É claro que você poderia ter promessas se resolvesse um erro, mas nesse caso provavelmente faria sentido tratá-lo como um erro de qualquer maneira, como mostra o último exemplo.

Outra vantagem de fazer isso, a destruição da matriz é mantida simples.

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

Ao invés de

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

Você pode argumentar que a !error1verificação é mais simples que uma instância de, mas você também precisa destruir as duas 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();

Keith
fonte
2

Em vez de rejeitar, resolva-o com um objeto. Você poderia fazer algo assim ao implementar a promessa

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

NuOne
fonte
11
Este parece um bom trabalho ao redor, não elegante, mas vai funcionar
Ensolarado Tambi
1

Eu acho que os seguintes oferece uma abordagem um pouco diferente ... comparar fn_fast_fail()com fn_slow_fail()... embora este último não falha como tal ... você pode verificar se um ou ambos ae bé uma instância de Errore throwque Errorse você quer que ele alcance o catchbloco (por exemplo if (b instanceof Error) { throw b; }). Veja o 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
drmrbrewer
fonte
0

Aqui está o meu costume 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;
  });
};

Comparado com Promise.all

  • Se todas as promessas forem resolvidas, ele será executado exatamente como o padrão.

  • Se uma ou mais promessas forem rejeitadas, ela retornará a primeira rejeitada da mesma forma que a padrão, mas, ao contrário, aguarda que todas as promessas sejam resolvidas / rejeitadas.

Para os corajosos, poderíamos mudar Promise.all():

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

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

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

CUIDADO . Em geral, nunca alteramos os built-ins, pois isso pode quebrar outras bibliotecas JS não relacionadas ou entrar em conflito com alterações futuras nos padrões JS.

My settledPromiseallé compatível com versões anteriores Promise.alle amplia sua funcionalidade.

Pessoas que estão desenvolvendo padrões - por que não incluí-lo em um novo padrão Promise?

Edward
fonte
0

Promise.allcom o uso de async/awaitabordagem moderna

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

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

const dataFromPromise1 = data[0]
const dataFromPromise2 = data[1]
Maksim Shamihulau
fonte
-1

Eu faria:

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
FRocha
fonte
-1

Você pode executar sua lógica sequencialmente através do executor síncrono nsynjs . Ele será pausado em cada promessa, aguardará resolução / rejeição e atribuirá o resultado da resolução à datapropriedade ou lançará uma exceção (para lidar com o que você precisará do bloco try / catch). Aqui está um exemplo:

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>

amaksr
fonte
-1

Eu tenho usado os seguintes códigos desde 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);
                }
            });
        });
    });
};

A assinatura de uso é exatamente como Promise.all. A principal diferença é que Promise.waitaguardará todas as promessas para terminar seus trabalhos.

user2273990
fonte
-1

Sei que esta pergunta tem muitas respostas e tenho certeza (se não todas) que estão corretas. No entanto, foi muito difícil para mim entender a lógica / fluxo dessas respostas.

Então olhei para a Implementação Original Promise.all()e tentei imitar essa lógica - com exceção de não interromper a execução se uma Promessa falhar.

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

Explicação:
- Faça um loop sobre a entrada promisesListe execute cada Promessa.
- Não importa se a promessa foi resolvida ou rejeitada: salve o resultado da promessa em uma resultmatriz de acordo com o index. Salve também o status de resolução / rejeição ( isSuccess).
- Depois que todas as promessas forem concluídas, retorne uma promessa com o resultado de todas as outras.

Exemplo de uso:

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
*/
Gil Epshtain
fonte
2
Não tente se reimplementar Promise.all, há muitas coisas que darão errado. Sua versão não lida com entradas vazias, por exemplo.
Bergi 08/03/19
-4

Não sei qual biblioteca de promessas você está usando, mas a maioria tem algo como all .

Edit: Ok, já que você deseja usar o ES6 simples sem bibliotecas externas, não existe esse método.

Em outras palavras: você precisa repetir suas promessas manualmente e resolver uma nova promessa combinada assim que todas as promessas forem cumpridas.

Sebastian S
fonte
Eu editei minha pergunta para esclarecer - Como o ES6 vem com promessas, eu gostaria de evitar o uso de outra biblioteca para o que eu acho que é uma funcionalidade básica. Eu acho que um bom lugar para obter a resposta seria copiar a fonte de uma das bibliotecas da promessa.
Nathan Hagen