sourcecode

일부 거절당하더라도 모든 약속이 완료될 때까지 기다립니다.

copyscript 2022. 12. 6. 22:05
반응형

일부 거절당하더라도 모든 약속이 완료될 때까지 기다립니다.

제가 한 , 한 세트, 한 세트, 한 세트, 한 세트, 한 세트, 한 세트, 한 세트, 한 세트, 한 세트.Promise네트워크 요구를 하고 있지만, 그 중 하나가 실패한다.

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

하나가 실패했는지 여부에 관계없이 이 모든 것이 완료될 때까지 기다립니다.리소스 없이 사용할 수 있는 네트워크 오류가 있을 수 있지만 얻을 수 있으면 계속 진행하기 전에 원하는 리소스입니다.네트워크 장애에 적절히 대처하고 싶다.

여유를 두지 않기 때문에 약속 라이브러리를 사용하지 않고 어떻게 대처하는 것이 좋을까요?

업데이트. 기본 제공 네이티브를 사용할 수 있습니다.

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

비슷한 답변이지만 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>

할 수 있는 를 들어, 에러를 사용합니다).undefinedt care 'Don't care', 'don't care',typeof " " " 입니다.result.message,result.toString().startsWith("Error:")의 개요)

벤자민의 답변은 이 문제를 해결하기 위한 훌륭한 추상화를 제공하지만, 저는 덜 추상화된 해결책을 원했습니다. 명확한 은 단순히 입니다..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));

한 걸음 더 나아가 다음과 같은 범용 어획 핸들러를 쓸 수 있습니다.

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

이 문제의 문제는 포착된 값이 포착되지 않은 값과 다른 인터페이스를 가지기 때문에 이를 정리하기 위해 다음과 같은 작업을 수행할 수 있다는 것입니다.

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

건조함을 유지하기 위해 벤자민의 답변을 얻을 수 있습니다.

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

두 번째 솔루션의 이점은 추상화되고 건조하다는 것입니다.단점은 더 많은 코드를 가지고 있다는 것입니다. 그리고 일관성을 유지하기 위해 모든 약속을 반영해야 한다는 것을 기억해야 합니다.

저는 제 솔루션의 특징을 명시적이고 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」를하지 않게 되었습니다.이것이 궁극적으로 사용하지 않기로 선택한 이유다.reflect.

이를 네이티브로 달성할 수 있는 함수에 대한 완성된 제안서가 바닐라 자바스크립트로 있습니다.Promise.allSettled4단계에 진입하여 ES2020에서 공식화되어 모든 현대 환경에 구현됩니다.이것은, 와 매우 유사합니다.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"
  }
]

이전 브라우저의 경우 여기에 사양 준거 폴리필이 있습니다.

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ 「 」 、 「 」 、 「 「 」
혹시 다른 방법을 찾고 계신지 모르니 요청하신 대로 하겠습니다.한 결과로 하며, 에러와 비슷하게 .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 ★★★★★★★★★★★★★★★★★」rejecteddiscloss.discloss.conf.

이 는 '를 하고 있습니다.catch이라는 뜻의 입니다. 않고 .Promise 것을 Promise시간이 초과된 후 해결되었을 때 표시됩니다.

이는 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,
        }))));
    };
}

거부하지 않고 개체로 해결합니다.약속을 이행할 때 이와 같은 일을 할 수 있습니다.

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

벤자민 그루엔바움의 대답은 물론 훌륭하다.하지만 추상화 수준이 모호해 보이는 Nathan Hagen의 관점도 알 수 있습니다.다음과 같은 짧은 객체 속성을 가진 경우e & v도움이 되진 않지만 물론 바뀔 수도 있죠

에는 Javascript라는 개체가 .Error 예를 들어 수 있습니다. 할 수 예요.instanceof Error가가잘는는는는는는는는는는는는는는는는

그래서 이 아이디어를 이용해서 이 문제에 대한 저의 견해를 말씀드리겠습니다.

기본적으로 오류를 파악합니다. 오류 유형이 Error가 아닌 경우 오류를 Error 개체 안에 래핑합니다.결과 어레이에는 확인된 값 또는 확인할 수 있는 오류 개체가 있습니다.

에서는 외부 할 때 할 수 .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.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

는 노드 9보다 이기 때문입니다.보다 훨씬 이전이기 때문에 ES2020 동작을 정확하게 재현하는 폴리필을 원했을 뿐입니다.Promise.allSettled등장)하고 있습니다. 중요한 ,.

const settle = (promise) => (promise instanceof Promise) ?
  promise.then(val => ({ value: val, status: "fulfilled" }),
               err => ({ reason: err, status: "rejected" })) :
  { value: promise, status: 'fulfilled' };

const allSettled = async (parr) => Promise.all(parr.map(settle));

ES 버전과 마찬가지로 약속과 약속 이외의 값의 혼합 배열을 처리합니다.로 되어 있는 것을 .{ status, value/reason }오브젝트를 네이티브버전으로 설정합니다.

저의 .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 표준 변경과 충돌할 수 있기 때문입니다.

★★★settledPromiseall와 하위 호환성이 있습니다.Promise.all기능을 확장합니다.

표준을 개발하고 있는 사람들 - 새로운 Promise 표준에 이 기준을 포함시키는 것은 어떨까요?

저는 최근에 당신이 필요로 하는 것을 허용하는 도서관을 지었습니다.병렬로 약속을 실행하고, 1개가 실패하면 프로세스가 계속 진행되며, 마지막에 오류를 포함한 모든 결과가 포함된 배열을 반환합니다.

https://www.npmjs.com/package/promise-ax

나는 그것이 누군가에게 도움이 되길 바라고 있다.

const { createPromise } = require('promise-ax');
const promiseAx = createPromise();
const promise1 = Promise.resolve(4);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, new Error("error")));
const promise3 = Promise.reject("error");
const promise4 = promiseAx.resolve(8);
const promise5 = promiseAx.reject("errorAx");
const asyncOperation = (time) => {
    return new Promise((resolve, reject) => {
        if (time < 0) {
            reject("reject");
        }
        setTimeout(() => {
            resolve(time);
        }, time);
    });
};
const promisesToMake = [promise1, promise2, promise3, promise4, promise5, asyncOperation(100)];
promiseAx.allSettled(promisesToMake).then((results) =>   results.forEach((result) => console.log(result)));
// Salida esperada:
// 4
// Error: error
// error
// 8
// errorAx
// 100

저는 다음과 같이 하겠습니다.

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

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모든 약속들이 끝날 때까지 기다릴 겁니다

이 질문에는 답이 많다는 것을 알고 있으며, 반드시 정답이 있어야 합니다(전부는 아니더라도).그러나 이러한 답변의 논리/흐름을 이해하는 것은 매우 어려웠다.

'의 구현'을.Promise.all()Promise 1개가 실패해도 실행을 멈추지 않는 것을 제외하고, 그 논리를 모방하려고 했습니다.

  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【약속하다
거부되었는지 의 결과를 에 저장합니다.result에 the에 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
*/

동기 실행 프로그램 nsynj를 통해 로직을 순차적으로 실행할 수 있습니다.각 약속에 대해 일시 중지하고 해결/거부를 기다린 후 해결 결과를 다음 중 하나에 할당합니다.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>

Promise.all async/await

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

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

const dataFromPromise1 = data[0]
const dataFromPromise2 = data[1]

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

편집: OK 외부 라이브러리 없이 일반 ES6를 사용하려는 경우 이러한 방법은 없습니다.

즉, 다음과 같습니다.당신은 당신의 약속을 수동으로 반복하고 모든 약속이 확정되는 즉시 새로운 결합된 약속을 해결해야 합니다.

언급URL : https://stackoverflow.com/questions/31424561/wait-until-all-promises-complete-even-if-some-rejected

반응형