Programming

Promise.all에서 오류 처리

procodes 2020. 5. 9. 10:36
반응형

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

Promise.all에 대한 .catch를 .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 ()을 작성해야합니다. 여기 내 프로젝트에서 사용하는 솔루션이 있습니다. 정상적인 결과로 오류가 반환됩니다. 모든 약속이 끝나면 오류를 걸러 낼 수 있습니다.

const Promise_all = promises => {
  return new Promise((resolve, reject) => {
    const results = [];
    let count = 0;
    promises.forEach((promise, idx) => {
      promise
        .catch(err => {
          return err;
        })
        .then(valueOrError => {
          results[idx] = valueOrError;
          count += 1;
          if (count === promises.length) resolve(results);
        });
    });
  });
};

const results = await Promise_all(promises)
const validResults = results.filter(result => !(result instanceof Error));

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


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


ES8을 사용하는 사람들은 비동기 함수를 사용하여 다음과 같은 작업을 수행 할 수 있습니다 .

var arrayOfPromises = state.routes.map(async function(route){
  try {
    return await route.handler.promiseHandler();
  } catch(e) {
    // Do something to handle the error.
    // Errored promises will return whatever you return here (undefined if you don't return anything).
  }
});

var resolvedPromises = await Promise.all(arrayOfPromises);

개별 약속 수준에서 거부를 처리 할 수 ​​있으므로 결과 배열에서 결과를 얻을 때 거부 된 배열 인덱스는입니다 undefined. 필요에 따라 해당 상황을 처리하고 나머지 결과를 사용할 수 있습니다.

여기에서 첫 번째 약속을 거부했기 때문에 약속이 정의되지 않았지만 두 번째 약속의 결과는 인덱스 1에 있습니다.

const manyPromises = Promise.all([func1(), func2()]).then(result => {
    console.log(result[0]);  // undefined
    console.log(result[1]);  // func2
});

function func1() {
    return new Promise( (res, rej) => rej('func1')).catch(err => {
        console.log('error handled', err);
    });
}

function func2() {
    return new Promise( (res, rej) => setTimeout(() => res('func2'), 500) );
}


당신은 고려 했습니까 Promise.prototype.finally()?

그것은 당신이 원하는 것을 정확하게 수행하도록 설계 된 것 같습니다-일부 약속이 거부 된 약속에 관계없이 모든 약속이 해결 (해결 / 거부)되면 기능을 실행하십시오.

로부터 MDN 문서 :

finally()에 관계없이 그 결과의 약속이 정착되면 일부 처리 또는 정리 작업을 수행하려는 경우 방법이 유용 할 수 있습니다.

finally()메소드는 호출과 매우 유사 .then(onFinally, onFinally)하지만 몇 가지 차이점이 있습니다.

함수를 인라인으로 만들 때 두 번 선언하거나 변수를 만들지 않고 한 번만 전달할 수 있습니다.

약속이 이행 또는 거부되었는지 여부를 결정하는 신뢰할 수있는 방법이 없기 때문에 최종 콜백은 인수를받지 않습니다. 이 사용 사례는 거부 사유 또는 이행 가치에 신경 쓰지 않고 정확하게 제공 할 필요가없는 경우를위한 것입니다.

달리 Promise.resolve(2).then(() => {}, () => {})(정의로 해석 될 것이다), Promise.resolve(2).finally(() => {})(2)와 유사하게 해석 될 달리 Promise.reject(3).then(() => {}, () => {})(정의로 이루어질 것이다), Promise.reject(3).finally(() => {})(3)이 거부된다.

== 폴백 ==

JavaScript 버전이 지원되지 않는 경우 Jake ArchibaldPromise.prototype.finally() 에서이 해결 방법을 사용할 수 있습니다 .Promise.all(promises.map(p => p.catch(() => undefined)));


또는 한 번의 실패가있을 때 해결 된 약속의 값을 특별히 신경 쓰지 않지만 여전히 실행되기를 원하는 경우, 다음과 같은 작업을 수행하여 정상적인 경우와 같은 약속으로 해결할 수 있습니다 그들 모두는 실패한 약속이 실패 할 때 성공하고 거절합니다.

function promiseNoReallyAll (promises) {
  return new Promise(
    async (resolve, reject) => {
      const failedPromises = []

      const successfulPromises = await Promise.all(
        promises.map(
          promise => promise.catch(error => {
            failedPromises.push(error)
          })
        )
      )

      if (failedPromises.length) {
        reject(failedPromises)
      } else {
        resolve(successfulPromises)
      }
    }
  )
}

약속 반환 함수는 실패를 포착하고 동의 한 값 (예 : error.message)을 반환하는 방식으로 항상 랩핑 할 수 있으므로 예외는 Promise.all 함수까지 롤업하여 비활성화하지 않습니다.

async function resetCache(ip) {

    try {

        const response = await axios.get(`http://${ip}/resetcache`);
        return response;

    }catch (e) {

        return {status: 'failure', reason: 'e.message'};
    }

}

그것이 Promise.all작동하도록 설계된 방법 입니다. 단일 약속이 있으면 reject()전체 방법이 즉시 실패합니다.

There are use cases where one might want to have the Promise.all allowing for promises to fail. To make this happen, simply don't use any reject() statements in your promise. However, to ensure your app/script does not freeze in case any single underlying promise never gets a response, you need to put a timeout on it.

function getThing(uid,branch){
    return new Promise(function (resolve, reject) {
        xhr.get().then(function(res) {
            if (res) {
                resolve(res);
            } 
            else {
                resolve(null);
            }
            setTimeout(function(){reject('timeout')},10000)
        }).catch(function(error) {
            resolve(null);
        });
    });
}

I wrote a npm library to deal with this problem more beautiful. https://github.com/wenshin/promiseallend

Install

npm i --save promiseallend

2017-02-25 new api, it's not break promise principles

const promiseAllEnd = require('promiseallend');

const promises = [Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)];
const promisesObj = {k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)};

// input promises with array
promiseAllEnd(promises, {
    unhandledRejection(error, index) {
        // error is the original error which is 'error'.
        // index is the index of array, it's a number.
        console.log(error, index);
    }
})
    // will call, data is `[1, undefined, 2]`
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

// input promises with object
promiseAllEnd(promisesObj, {
    unhandledRejection(error, prop) {
        // error is the original error.
        // key is the property of object.
        console.log(error, prop);
    }
})
    // will call, data is `{k1: 1, k3: 2}`
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

// the same to `Promise.all`
promiseAllEnd(promises, {requireConfig: true})
    // will call, `error.detail` is 'error', `error.key` is number 1.
    .catch(error => console.log(error.detail))

// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [false, true, false]})
    // won't call
    .then(data => console.log(data))
    // will call, `error.detail` is 'error', `error.key` is number 1.
    .catch(error => console.log(error.detail))

// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [true, false, false]})
    // will call, data is `[1, undefined, 2]`.
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

————————————————————————————————

Old bad api, do not use it!

let promiseAllEnd = require('promiseallend');

// input promises with array
promiseAllEnd([Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)])
    .then(data => console.log(data)) // [1, undefined, 2]
    .catch(error => console.log(error.errorsByKey)) // {1: 'error'}

// input promises with object
promiseAllEnd({k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)})
    .then(data => console.log(data)) // {k1: 1, k3: 2}
    .catch(error => console.log(error.errorsByKey)) // {k2: 'error'}

참고URL : https://stackoverflow.com/questions/30362733/handling-errors-in-promise-all

반응형