일부는 거절하더라도 모든 약속이 완료 될 때까지 기다리십시오
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
이에 대한 어떤 방을 떠나지 않아하는 약속 라이브러리를 사용하지 않고,이를 처리하기위한 권장 패턴은 무엇인가?
물론, 당신은 단지 필요합니다 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");
});
비슷한 대답이지만 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:")
등)
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
.
바닐라 자바 스크립트에서이를 기본적으로 수행 할 수있는 함수에 대한 제안 이 있습니다 Promise.allSettled
. 현재 4 단계에 있으며 공식 사양으로 만들 가능성이 높습니다. 이 다른 답변 의 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');
이것이 사양의 일부가되고 브라우저가이를 구현하면 라이브러리없이 최신 브라우저에서 사용할 수 있습니다 .
Chrome 76으로 배송되었으므로 다음 스 니펫이 문제없이 실행됩니다.
Promise.allSettled([
Promise.resolve('a'),
Promise.reject('b')
])
.then(console.log);
산출:
[
{
"status": "fulfilled",
"value": "a"
},
{
"status": "rejected",
"reason": "b"
}
]
저는 벤자민의 대답을 정말 좋아합니다. 그리고 어떻게 그가 기본적으로 모든 약속을 항상 해결하지만 때로는 오류가있는 결과로 바꾸는가. :)
대안을 찾고있는 경우를 대비하여 귀하의 요청에 대한 나의 시도는 다음과 같습니다. 이 방법은 단순히 오류를 유효한 결과로 취급하며, 다음과 유사하게 코딩 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);
});
}
var err;
Promise.all([
promiseOne().catch(function(error) { err = error;}),
promiseTwo().catch(function(error) { err = error;})
]).then(function() {
if (err) {
throw err;
}
});
이 Promise.all
거부 된 약속을 삼켜 약속을 모두 해결 한 때 반환됩니다, 그래서 변수에 오류를 저장합니다. 그런 다음 오류를 다시 발생 시키거나 무엇이든 할 수 있습니다. 이런 식으로, 당신이 첫 번째 거부 대신 마지막 거부를 얻을 것이라고 생각합니다.
나는 같은 문제가 있었고 다음과 같은 방법으로 해결했습니다.
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
.
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,
}))));
};
}
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();
나는 ... 다음 이벤트 약간 다른 접근 방식을 생각 비교 fn_fast_fail()
와 fn_slow_fail()
... 후자는 같은 실패하지 않습니다하지만 ... 당신은 하나 또는 두 경우 확인할 수 있습니다 a
와 b
의 인스턴스 Error
와 throw
것을 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
여기 내 습관이 있습니다 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 표준에이를 포함시키지 않는 이유는 무엇입니까?
거부하는 대신 객체로 해결하십시오. 약속을 이행 할 때 이와 같은 일을 할 수 있습니다
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))
나는 할것이다:
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
동기식 실행기 nsynjs 를 통해 순차적으로 논리를 실행할 수 있습니다 . 각 약속마다 일시 중지되고, 해결 / 거절을 기다린 다음, 해결 결과를 data
속성에 할당 하거나 예외를 처리합니다 (시도 / 잡기 차단이 필요한 처리). 예를 들면 다음과 같습니다.
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>
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
모든 약속이 작업을 마치기를 기다릴 것입니다.
나는이 질문에 많은 답변이 있음을 알고 있으며 (모두는 아니지만) 정확해야합니다. 그러나 이러한 답변의 논리 / 흐름을 이해하는 것은 매우 어렵습니다.
그래서 나는 Original Implementation을 살펴 보았고 Promise.all()
, 하나의 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의 결과를에 따라 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
*/
어떤 약속 라이브러리를 사용하고 있는지 모르겠지만 대부분 allSettled 와 같은 것이 있습니다.
편집 : 외부 라이브러리없이 일반 ES6을 사용하고 싶기 때문에 그러한 방법은 없습니다.
즉, 약속을 수동으로 반복 하고 모든 약속이 확정되는 즉시 새로운 결합 된 약속을 해결해야합니다 .
참고 URL : https://stackoverflow.com/questions/31424561/wait-until-all-promises-complete-even-if-some-rejected
'Programming' 카테고리의 다른 글
"스레드 안전"코드 란 무엇입니까? (0) | 2020.03.04 |
---|---|
파이썬에서 숫자의 목록을 합치십시오 (0) | 2020.03.04 |
Java에서 StringBuilder를 사용하는 경우 (0) | 2020.03.04 |
자식 체리 픽 중단? (0) | 2020.03.04 |
IEnumerable 또는 List가 아닌 ICollection을 사용하는 이유 (0) | 2020.03.03 |