분류 javascript

JS Promises: race vs all vs allSettled(2)

컨텐츠 정보

  • 조회 181 (작성일 )

본문

Promise.all 메소드 


여러 개의 병렬, 비동기식 호출을 처리해야 하는 경우에 적합합니다. 모든 방법을 사용하면 케이크를 먹고 먹을 수도 있습니다.


정의에 따라 Promise.all은 다음 조건 중 하나가 충족 될 때까지 모든 Promise을 실행합니다.

  • 그들 모두는 해결되며, 결과적으로 방법에 의해 반환 된 Promise을 해결합니다.
  • 그들 중 하나는 실패한 Promise을 즉시 거절 할 것입니다

Promise.all에서 기억해야 할 것은 마지막 요점입니다. 부분 실패를 처리 할 수 없습니다. Promise 중 하나가 거부되면 전체 프로세스가 중지되고 실패 콜백이 호출됩니다. 거부 된 Promise이 미션 크리티컬한 작업을 수행하지 않고 내용이 누락 될 수 있는 경우에는 이상적이지 않습니다.


기본 데이터베이스에서 데이터를 가져오고 외부 서비스를 사용하여 결과를 보강하는 검색 서비스를 생각해보십시오. 이러한 외부 서비스는 필요하지 않으며 가능한 경우 추가 정보를 제공 할 수 있도록 도와줍니다.


이러한 타사 서비스가 실패하면 검색 프로세스 중에 이 방법이 실패하여 검색 프로세스가 중지되고 유효한 검색 결과가 사용자에게 반환 되지 않습니다.


여기에는 내부 논리가 모든 약속을 실행할 수 있도록 하고, 가능한 거부를 무시하고 싶습니다.


Enter Promise.allSettled 


위와 같은 사용 사례에서 오는 경우 모든 문제에 대한 솔루션입니다. 안타깝게도 이 방법은 아직 JavaScript의 일부가 아닙니다. 설명해 드리겠습니다 : 고려 및 검토 중인 제안 된 추가 사항입니다. 그러나 슬프게도 아직 언어의 기본 부분이 아닙니다.


즉, 외부 구현의 수를 감안할 때 어쨌든 그것을 덮는 것에 대해 생각했습니다.


요점은 이전 방법과 달리 첫 번째 약속이 거부 된 후에도 실패하지 않고 대신 값 목록을 반환한다는 것입니다. 이 값은 두 가지 속성을 가진 객체입니다.

  1. 반품 약속 상태 ( '거부 됨'또는 '완료 됨')
  2. 이행 된 약속의 가치 또는 거부 된 약속의 이유
var allSettled = require('promise.allsettled');
 
var resolved = Promise.resolve(42);
var rejected = Promise.reject(-1);
 
allSettled([resolved, rejected]).then(function (results) {
    assert.deepEqual(results, [
        { status: 'fulfilled', value: 42 },
        { status: 'rejected', reason: -1 }
    ]);
});
 
allSettled.shim(); // will be a no-op if not needed
 
Promise.allSettled([resolved, rejected]).then(function (results) {
    assert.deepEqual(results, [
        { status: 'fulfilled', value: 42 },
        { status: 'rejected', reason: -1 }
    ]);
});

위의 예제는 구현이 실행 중이며 타사 라이브러리 약속입니다. 모든 것을 염두에 두었지만 최신 버전의 사양을 준수합니다.


참고 : 메소드 이름을 혼동하지 마십시오. 많은 사람들이 "allSettled"가 "allResolved"와 동일하다고 생각합니다. 약속은 해결되거나 거부되면 해결되며, 그렇지 않으면 보류 중입니다. 자세한 내용은 Promise가 가질 수 있는 상태와 운명의 전체 목록을 확인하십시오.


첫 번째로 해결 된 약속에서 멈추고 싶다면 어떻게 해야 합니까? 


첫 번째 약속이 실패하면 (Promise.all처럼) 중지하는 대신 첫 번째 약속이 해결되면 중지하고 싶을 경우 어떻게 해야 합니까?


이것은 Promise 객체가 레이스 방식을 사용하여 여러 약속을 처리 할 수 있는 다른 방법입니다. 모든 약속을 해결하는 대신 실제로 첫 번째 약속이 완료 될 때까지 기다리면 실패하거나 성공합니다. 약속이 해결되었는지 아니면 거절되었는지.


예, 제목이 약간 부정적입니다.이 방법은 Promise.all과 마찬가지로 거부 된 약속이 있으면 프로세스가 중지되기 때문입니다.


그러나 주의를 기울이지 말고, 왜 여러 개의 약속을 병렬로 실행하고 첫 번째로 확정 된 약속의 결과 만 가져 오려고 하는지 생각해 봅시다.


언제 race를 사용합니까? 


이 방법을 사용하려는 이유에 대한 몇 가지 예가 있습니다. 지금 두 가지를 드리겠습니다.


1 번 : 성능 점검 


예를 들어, 성능이 플랫폼의 중요한 부분 인 경우 데이터 원본의 사본을 여러 개 갖고 싶은 경우 네트워크 트래픽이나 기타 외부 요인에 따라 가장 빠른 데이터 원본을 얻기 위해 모든 쿼리를 시도 할 수 있습니다.


약속 및 경쟁 방법을 사용하면 모든 소스에서 데이터를 가져 오는 데 집중하고 JavaScript가 나머지를 처리하도록 할 수 있습니다.


const request = require("request");

let sources = ["http://www.bing.com", "http://www.yahoo.com", "http://www.google.com" ];

let checks = sources.map( s => {
  return new Promise( (res, rej) => {
    let start = (new Date()).getTime()
    request.get(s, (err, resp) => {
        let end = (new Date()).getTime()
        if(err) return rej(err)
        res({
            datasource: s,
            time: end - start
        })
    })
  })
})

Promise.race(checks).then( r => {
  console.log("Fastest source: ", r.datasource, " resolved in: ", r.time, " ms")
})

코드는 약간 기본적이며 코드를 향상 시키는 방법은 여러 가지가 있지만 내 요점을 보여줍니다. 비동기 해상도를 처리하기 위해 특정 논리를 추가하지 않고도 어떤 데이터 소스가 가장 빠른지 확인하고 있습니다. 결과를 비교하려면 Promise.allSettled 호출에 대해 이를 변경해야 합니다.


번호 2 : 로딩 표시기, 표시해야 합니까? 


이 메소드 사용을 고려할 수 있는 또 다른 예는 UI에 로드 표시기를 표시할지 여부를 결정할 때입니다. SPA를 만들 때 가장 좋은 방법은 비동기 호출이 사용자에게 로드 표시기를 트리거하여 문제가 발생했음을 알리는 것입니다.


그러나 기본 요청이 매우 빠르게 발생하는 경우에는 이 규칙이 적합하지 않습니다. UI에 포함 된 모든 메시지가 너무 빨리 지나가는 메시지 깜박임 일 수 있기 때문입니다. 로드 시간은 표시기를 표시 할 시기와 표시 없이 요청을 수행 할 시기를 알 수 있는 규칙을 작성하기에 너무 많은 항목에 따라 달라질 수 있습니다.


거부 및 해결 개념을 가지고 다음과 같은 것을 가질 수 있습니다.


function yourAsynchronousRequest(params) {
  return new Promise((resolve, reject) => {
       //here is your request code, it'll resolve once it gets the actual data from the server
  });
}

function showDataToUser(params) {
  return yourAsynchronousRequest(params).then( data => console.log("data fetched:", data));
}

function timeout() {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject(), TIMEOUTLIMIT); //TIMEOUTLIMIT is a constant you configured
  });
}

function showLoadingIndicator() {
  console.log("please wait...")
}

Promise.race([showDataToUser(), timeout()]).catch(showLoadingIndicator);

이제 경쟁은 실제 비동기 요청과 시간 제한이 리미터로 설정되었습니다. 이제 로드 표시기를 표시할지 여부를 결정하는 논리가 경쟁 방법 뒤에 숨겨져 있습니다.