일부는 거절하더라도 모든 약속이 완료 될 때까지 기다리십시오


405

Promise네트워크 요청을 하는 일련의 s 가 있다고 가정 해 봅시다 .

// 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이에 대한 어떤 방을 떠나지 않아하는 약속 라이브러리를 사용하지 않고,이를 처리하기위한 권장 패턴은 무엇인가?


거부 된 약속에 대해 결과 배열로 무엇을 반환해야합니까?
Kuba Wyrostek

9
ES6는 그러한 방법을 지원하지 않으며 (현재 Bluebird보다 느리다 ). 또한 모든 브라우저 나 엔진이 아직이를 지원하지는 않습니다. 나는 것이 강하게 와 함께 제공 블루 버드, 사용하는 것이 좋습니다 allSettled당신은 자신의 롤하지 않고있는 만족 당신의 필요를.
Dan Pantry

@ KubaWyrostek Promise.all 에이 동작이없는 이유를 제기한다고 생각합니다. 이것이 작동하는 방식은 아니지만 Promise.all은 결코 실패하지 않는 특별한 약속을 반환해야하며, 실패한 약속을 나타내는 인수로 발생한 오류를 얻게 될 수도 있습니다.
Nathan Hagen

Dan이 공유 한 내용에 추가하기 위해 "반사"기능을 통해 블루 버드의 allSettled / settleAll과 같은 기능을 활용할 수 있습니다.
user3344977

2
@ 콜리 : 흠, 그렇게 생각하지 않습니다. 하나의 약속 Promise.all이 거부되는 즉시 거부하므로 제안 된 관용구가 모든 약속이 확정되었다고 보장하지는 않습니다.
Jörg W Mittag

답변:


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

4
내 자신의 문제에 대한 응답으로 다음 npm 패키지를 만들었습니다. github.com/Bucabug/promise-reflect npmjs.com/package/promise-reflect
SamF

2
나는 얼마 전에이 문제 a를 우연히 나는 그것을이 NPM 패키지를 만든 : npmjs.com/package/promise-all-soft-fail
velocity_distance

5
reflect컴퓨터 과학에서이 단어 는 일반적인 단어입니까? 위키 백과 등의 설명이있는 곳으로 링크 해 주시겠습니까? 열심히 찾고 Promise.all not even first reject있었지만 "반사"를 검색하는 방법을 몰랐습니다. ES6에 Promise.reflect"Promise.all이지만 실제로는 전부"와 같은 것이 있어야합니까 ?
Noitidart

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.message, result.toString().startsWith("Error:")등)


1
@KarlBateman 혼란 스럽습니다. 이 .map(p => p.catch(e => e))부분에서 모든 거부가 해결 된 값으로 바뀌므로 주문 함수가 해결하거나 거부하는 순서는 중요하지 않으므로 Promise.all개별 함수가 수행하는 시간에 관계없이 개별 함수가 해결 또는 거부 할 때까지 모든 것이 완료 될 때 까지 기다립니다. 시도 해봐.
jib

39
.catch(e => console.log(e));이 실패하지 않기 때문에 호출되지 않습니다
fregante

4
@ bfred.it 맞습니다. 약속 체인을 종료하는 catch것이 일반적으로 좋은 방법 이지만 IMHO .
jib

2
@SuhailGupta 오류를 포착하여 e일반 (성공) 값으로 반환합니다. p.catch(function(e) { return e; })더 짧은 것과 같습니다 . return암시 적입니다.
jib

1
@JustinReusnow는 이미 의견을 다루었습니다. 나중에 코드를 추가 할 경우 항상 체인을 종료하는 것이 좋습니다.
jib

71

Benjamin의 답변은이 문제를 해결하기위한 훌륭한 추상화를 제공하지만 덜 추상화 된 솔루션을 원했습니다. 이 문제를 해결하는 명시적인 방법은 단순히 .catch내부 약속을 호출 하고 콜백에서 오류를 반환하는 것입니다.

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

한 단계 더 나아가 다음과 같은 일반적인 catch 처리기를 작성할 수 있습니다.

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

이 문제는 catch 된 값이 catch되지 않은 값과 다른 인터페이스를 가지므로이를 정리하기 위해 다음과 같은 작업을 수행 할 수 있습니다.

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

그런 다음 DRY를 유지하려면 Benjamin의 답변을 얻습니다.

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

두 번째 솔루션의 장점은 추상화되어 DRY라는 것입니다. 단점은 더 많은 코드가 있으며 일을 일관성있게 만들기 위해 모든 약속을 반영해야한다는 것입니다.

내 솔루션을 명시 적 및 KISS로 특성화하지만 실제로는 덜 견고합니다. 인터페이스는 약속의 성공 여부를 정확하게 알고 있다고 보증하지 않습니다.

예를 들어 다음이있을 수 있습니다.

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

제 경우에는 오류나 오류가 무엇인지 알 필요가 없습니다. 가치가 있는지 여부 만 신경 쓰면됩니다. 약속을 생성하는 함수가 특정 오류를 기록하는 것에 대해 걱정하도록하겠습니다.

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

이렇게하면 나머지 응용 프로그램은 원하는 경우 오류를 무시하고 원하는 경우 정의되지 않은 값으로 처리 할 수 ​​있습니다.

나는 높은 수준의 기능이 안전하게 실패하고 그 종속성이 실패한 이유에 대한 세부 사항에 대해 걱정하지 않기를 원하며, 그 상충 관계를 만들어야 할 때 KISS를 DRY보다 선호합니다 reflect.


1
@ Benjamin @Nathan의 솔루션은 매우 간단하고 관용적이라고 생각 Promise합니다. 당신이하는 동안 reflect코드 재사용을 향상, 그것은 또한 추상화의 다른 수준을 설정합니다. Nathan의 답변은 지금까지 귀하에 비해 약간의 공감대를 받았기 때문에 이것이 아직 인식하지 못한 솔루션에 문제가 있는지 궁금합니다.

2
@ LUH3417이 솔루션은 오류를 값으로 취급하고 오류를 비 오류와 분리하지 않기 때문에 개념적으로 소리가 작습니다. 예를 들어, 약속 중 하나가 합법적으로 던져 질 수있는 (전적으로 가능한) 값으로 해결되면 이것은 상당히 나빠집니다.
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

거기에있다 완성 된 제안 바닐라 자바 스크립트, 기본적으로이 작업을 수행 할 수있는 기능 : 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

2
좋아, 정착 은 실제로 더 나은 이름이 될 것입니다. :)
Kuba Wyrostek

이것은 명백한 약속 건설 반 패턴과 매우 비슷합니다. 그러한 함수를 직접 작성해서는 안되지만 라이브러리가 제공하는 기능을 사용하십시오 (기본 ES6은 약간 빈약합니다).
Bergi

Promise생성자를 올바르게 사용 하고 피할 수 var resolve있습니까?
Bergi

Bergi, 당신이 필요하다고 생각하지만 답변을 자유롭게 변경하십시오.
Kuba Wyrostek

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.all냅니다. 나는 모든 약속이 불려 졌을 때들을 수있는 방법을 찾고 있지만, 직접 약속하지는 않습니다. 감사.
SudoPlz

@SudoPlz는이 방법 all()을 사용하여 모든 약속의 이행을 기다리거나 그 중 하나 이상을 거부합니다.
user1016265

그것은 사실이지만, 기다리지 않고 실제로 프로세스를 호출 / 시작 / 시작합니다. 가능하지 않은 다른 약속을 해고하고 싶다면 .all모든 것을 발사하십시오.
SudoPlz

@SudoPlz는 이것이 귀하의 의견을 바꿉니다 희망 jsfiddle.net/d1z1vey5
user1016265

3
나는 정정되었다. 지금까지 나는 누군가가 (일명 then또는 .all전화) 호출 할 때만 약속이 실행된다고 생각 했지만 생성 될 때 실행됩니다.
SudoPlz

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

Benjamin Gruenbaum의 대답은 물론 훌륭합니다. 그러나 또한 추상화 수준의 Nathan Hagen 관점이 모호한 것처럼 보입니다. 짧은 객체 속성과 같은e & v 것도 도움이되지 않지만 물론 변경 될 수 있습니다.

Javascript에는이라는 표준 Error 객체가 있습니다 Error. 이상적으로 항상 인스턴스 / 하위 항목을 던집니다. 장점은 할 수 있다는 것입니다instanceof Error 있고 오류가 있다는 것을 알고 있다는 것입니다.

그래서이 아이디어를 사용하여 여기에 문제가 있습니다.

기본적으로 오류를 잡으십시오. 오류가 오류 유형이 아닌 경우 오류를 오류 오브젝트 안에 랩하십시오. 결과 배열에는 확인 된 값 또는 확인할 수있는 오류 개체가 있습니다.

catch 내부의 instance는 reject("error")대신에 외부 라이브러리를 사용했을 경우 입니다 reject(new Error("error")).

물론 오류를 해결했다는 약속을 할 수는 있지만,이 경우 마지막 예에서 볼 수 있듯이 오류로 취급하는 것이 좋습니다.

이를 수행하는 또 다른 이점은 배열을 간단하게 유지하는 것입니다.

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

거부하는 대신 객체로 해결하십시오. 약속을 이행 할 때 이와 같은 일을 할 수 있습니다

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()... 후자는 같은 실패하지 않습니다하지만 ... 당신은 하나 또는 두 경우 확인할 수 있습니다 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.all():

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

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

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

조심하십시오 . 일반적으로 우리는 내장되지 않은 JS 라이브러리를 손상 시키거나 향후 JS 표준 변경과 충돌 할 수 있으므로 내장을 변경하지 않습니다.

My 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속성에 할당 하거나 예외를 throw합니다 (시도 / 잡기 차단이 필요한 처리). 예를 들면 다음과 같습니다.

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

나는이 질문에 많은 답변이 있다는 것을 알고 있으며 (모두는 아니지만) 정확해야합니다. 그러나 이러한 답변의 논리 / 흐름을 이해하는 것은 매우 어렵습니다.

그래서 나는 Original Implementation을 살펴 보았고 Promise.all(), 하나의 약속이 실패했을 때 실행을 멈추지 않는 것을 제외하고는 그 논리를 모방하려고했습니다.

  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의 결과를에 따라 result배열에 저장 하십시오 index. 해결 / 거부 상태 ( isSuccess) 도 저장하십시오 .
-모든 약속이 완료되면 다른 약속의 결과와 함께 하나의 약속을 반환하십시오.

사용 예 :

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자신 을 다시 구현하려고 시도하지 마십시오 . 잘못 될 일이 너무 많습니다. 예를 들어 버전에서 빈 입력을 처리하지 않습니다.
Bergi

-4

어떤 약속 라이브러리를 사용하고 있는지 모르겠지만 대부분 all 과 같은 것이 있습니다. 있습니다.

편집 : Ok 외부 라이브러리없이 일반 ES6을 사용하고 싶기 때문에 그러한 방법은 없습니다.

즉, 약속을 수동으로 반복 하고 모든 약속이 확정되는 즉시 새로운 결합 된 약속을 해결해야합니다 .


ES6에는 약속이 포함되어 있기 때문에 기본 기능이라고 생각하는 것에 대해 다른 라이브러리를 사용하지 않는 것이 좋습니다. 대답을 얻을 수있는 좋은 곳은 약속 라이브러리 중 하나에서 소스를 복사하는 것입니다.
Nathan Hagen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.