[javascript] Promise.all에서 오류 처리

해결하려는 약속의 배열이 있습니다. Promise.all(arrayOfPromises);

약속 체인을 계속 진행합니다. 이런 식으로 보인다

existingPromiseChain = existingPromiseChain.then(function() {
  var arrayOfPromises = state.routes.map(function(route){
    return route.handler.promiseHandler();
  });
  return Promise.all(arrayOfPromises)
});

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
  // do stuff with my array of resolved promises, eventually ending with a res.send();
});

오류가 발생할 경우 개별 약속을 처리하기 위해 catch 문을 추가하고 싶지만 시도 할 때 Promise.all찾은 첫 번째 오류를 반환하고 (나머지는 무시) 나머지 약속에서 데이터를 가져올 수 없습니다. 배열 (오류가 없었습니다).

나는 .. 같은 것을 시도했다.

existingPromiseChain = existingPromiseChain.then(function() {
      var arrayOfPromises = state.routes.map(function(route){
        return route.handler.promiseHandler()
          .then(function(data) {
             return data;
          })
          .catch(function(err) {
             return err
          });
      });
      return Promise.all(arrayOfPromises)
    });

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
      // do stuff with my array of resolved promises, eventually ending with a res.send();
});

그러나 그것은 해결되지 않습니다.

감사!

편집하다:

아래 답변이 완전히 사실이라고 말하면 코드는 다른 이유로 인해 깨졌습니다. 누군가 관심이 있다면, 이것이 내가 끝낸 해결책입니다 …

Node Express 서버 체인

serverSidePromiseChain
    .then(function(AppRouter) {
        var arrayOfPromises = state.routes.map(function(route) {
            return route.async();
        });
        Promise.all(arrayOfPromises)
            .catch(function(err) {
                // log that I have an error, return the entire array;
                console.log('A promise failed to resolve', err);
                return arrayOfPromises;
            })
            .then(function(arrayOfPromises) {
                // full array of resolved promises;
            })
    };

API 호출 (route.async 호출)

return async()
    .then(function(result) {
        // dispatch a success
        return result;
    })
    .catch(function(err) {
        // dispatch a failure and throw error
        throw err;
    });

.catchfor를 Promise.all앞에 두는 것은 .then원래 약속에서 오류를 잡아 내고 전체 배열을 다음으로 되돌릴 목적으로 사용되었습니다..then

감사!



답변

Promise.all전부 또는 아무것도 아니다. 배열의 모든 약속이 해결되면 해결하거나 약속 중 하나 가 거부되는 즉시 거부합니다. 즉, 모든 해결 된 값의 배열로 해결되거나 단일 오류로 거부됩니다.

일부 라이브러리에는이라는 것이 Promise.when있는데, 대신 배열의 모든 약속이 해결되거나 거부 될 때까지 기다리는 것이지만 익숙하지 않으며 ES6에는 없습니다.

귀하의 코드

귀하의 픽스가 작동해야한다는 다른 사람들의 의견에 동의합니다. 성공적인 값과 오류 개체가 혼합 된 배열로 해결해야합니다. 성공 경로에 오류 객체를 전달하는 것은 드문 일이지만 코드가 예상한다고 가정하면 아무런 문제가 없습니다.

내가 “해결하지 못하는”이유를 생각할 수있는 유일한 이유는 우리에게 보여주지 않는 코드에서 실패하고 이것에 대한 오류 메시지가 표시되지 않는 이유는이 약속 체인이 최종으로 끝나지 않기 때문입니다 잡으십시오 (어쨌든 우리에게 보여주고있는 한).

귀하의 예에서 “기존 체인”을 제외하고 캐치로 체인을 종료 할 자유를 얻었습니다. 이것은 당신에게 맞지 않을 수도 있지만, 이것을 읽는 사람들에게는 항상 체인을 반환하거나 종료하는 것이 중요합니다. 또는 잠재적 오류, 심지어 코딩 오류조차도 숨겨 질 것입니다 (여기서 내가 여기에서 일어난 것으로 의심되는 것) :

Promise.all(state.routes.map(function(route) {
  return route.handler.promiseHandler().catch(function(err) {
    return err;
  });
}))
.then(function(arrayOfValuesOrErrors) {
  // handling of my array containing values and/or errors. 
})
.catch(function(err) {
  console.log(err.message); // some coding error in handling happened
});


답변

새로운 답변

const results = await Promise.all(promises.map(p => p.catch(e => e)));
const validResults = results.filter(result => !(result instanceof Error));

미래 약속 API


답변

Promise.all루프 를 계속하기 위해 (Promise가 거부하더라도)라는 유틸리티 함수를 작성했습니다 executeAllPromises. 이 유틸리티 함수는 results및 로 객체를 반환합니다 errors.

아이디어는 당신이 전달하는 모든 약속 executeAllPromises이 항상 해결할 새로운 약속으로 포장 될 것입니다. 새로운 Promise는 2 개의 스팟이있는 어레이로 해결됩니다. 첫 번째 스팟은 해결 값 (있는 경우)을 보유하고 두 번째 스팟은 오류를 유지합니다 (랩핑 된 약속이 거부 된 경우).

마지막 단계로서이 executeAllPromises랩 약속 모든 값을 축적하는 어레이의 최종 개체를 반환 results하고 대한 배열 errors.

코드는 다음과 같습니다.

function executeAllPromises(promises) {
  // Wrap all Promises in a Promise that will always "resolve"
  var resolvingPromises = promises.map(function(promise) {
    return new Promise(function(resolve) {
      var payload = new Array(2);
      promise.then(function(result) {
          payload[0] = result;
        })
        .catch(function(error) {
          payload[1] = error;
        })
        .then(function() {
          /*
           * The wrapped Promise returns an array:
           * The first position in the array holds the result (if any)
           * The second position in the array holds the error (if any)
           */
          resolve(payload);
        });
    });
  });

  var errors = [];
  var results = [];

  // Execute all wrapped Promises
  return Promise.all(resolvingPromises)
    .then(function(items) {
      items.forEach(function(payload) {
        if (payload[1]) {
          errors.push(payload[1]);
        } else {
          results.push(payload[0]);
        }
      });

      return {
        errors: errors,
        results: results
      };
    });
}

var myPromises = [
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.reject(new Error('3')),
  Promise.resolve(4),
  Promise.reject(new Error('5'))
];

executeAllPromises(myPromises).then(function(items) {
  // Result
  var errors = items.errors.map(function(error) {
    return error.message
  }).join(',');
  var results = items.results.join(',');

  console.log(`Executed all ${myPromises.length} Promises:`);
  console.log(`— ${items.results.length} Promises were successful: ${results}`);
  console.log(`— ${items.errors.length} Promises failed: ${errors}`);
});


답변

ES2020 에는 Promise 유형에 대한 새로운 방법이 도입되었습니다. Promise.allSettled()
Promise.allSettled는 모든 입력 약속이 확정 될 때 신호를 보내므로 약속이 이행 또는 거부됩니다. 이것은 약속 상태에 신경 쓰지 않고 성공 여부에 관계없이 작업이 완료되는 시점을 알고 싶을 때 유용합니다.

const promises = [
  fetch('/api-call-1'),
  fetch('/api-call-2'),
  fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.

const result = await Promise.allSettled(promises);
console.log(result.map(x=>s.status));
// ['fulfilled', 'fulfilled', 'rejected']

v8 블로그 게시물 https://v8.dev/features/promise-combinators 에서 자세히 알아보십시오.


답변

@jib이 말했듯이

Promise.all 전부 또는 아무것도 아니다.

그러나 “허용 된”특정 약속을 제어 할 수 있으며 계속 진행하고 싶습니다 .then.

예를 들어.

  Promise.all([
    doMustAsyncTask1,
    doMustAsyncTask2,
    doOptionalAsyncTask
    .catch(err => {
      if( /* err non-critical */) {
        return
      }
      // if critical then fail
      throw err
    })
  ])
  .then(([ mustRes1, mustRes2, optionalRes ]) => {
    // proceed to work with results
  })


답변

q 라이브러리 https://github.com/kriskowal/q 를 사용하면
이 문제를 해결할 수있는 q.allSettled () 메소드가있어 풀 파일 또는 거부 상태에 따라 모든 약속을 처리 할 수 ​​있습니다.

existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
  return route.handler.promiseHandler();
});
return q.allSettled(arrayOfPromises)
});

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
//so here you have all your promises the fulfilled and the rejected ones
// you can check the state of each promise
arrayResolved.forEach(function(item){
   if(item.state === 'fulfilled'){ // 'rejected' for rejected promises
     //do somthing
   } else {
     // do something else
   }
})
// do stuff with my array of resolved promises, eventually ending with a res.send();
});


답변

비동기 대기 사용-

여기서 하나의 비동기 함수 func1이 해결 된 값을 리턴하고, func2가 오류를 발생시키고이 상황에서 널을 리턴하면 원하는 방식으로 처리하고 그에 따라 리턴 할 수 있습니다.

const callingFunction  = async () => {
    const manyPromises = await Promise.all([func1(), func2()]);
    console.log(manyPromises);
}


const func1 = async () => {
    return 'func1'
}

const func2 = async () => {
    try {
        let x;
        if (!x) throw "x value not present"
    } catch(err) {
       return null
    }
}

callingFunction();

출력은-[ ‘func1’, null]