101 시리즈: 프라미스 #2: 현재 프라미스 상태를 얻고 자신만의 프라미스 큐를 만드는 방법?
더 복잡한 주제에 도달하기 전에 Promise 위에 만들 수 있는 도구와 유틸리티 기능에 대해 생각해 볼 것을 제안합니다. 이 기사에서는 현재 약속 상태를 가져오고 약속 대기열을 만드는 방법에 대해 생각해 보겠습니다.
TL&DR
약속 상태 확인
const pending = {
state: 'pending',
};
function getPromiseState(promise) {
// We put `pending` promise after the promise to test,
// which forces .race to test `promise` first
return Promise.race([promise, pending]).then(
(value) => {
if (value === pending) {
return value;
}
return {
state: 'resolved',
value
};
},
(reason) => ({ state: 'rejected', reason })
);
}
약속 대기열
class Queue {
_queue = Promise.resolve();
enqueue(fn) {
const result = this._queue.then(fn);
this._queue = result.then(() => {}, () => {});
// we changed return result to return result.then()
// to re-arm the promise
return result.then();
}
wait() {
return this._queue;
}
}
현재 약속 상태를 얻는 방법
약속을 생성한 후에는 약속이 여전히
pending
, fulfilled
또는 rejected
인지 런타임에 정보를 얻을 수 없습니다.디버거에서 현재 약속 상태를 볼 수 있습니다.
디버거에서 Symbol
[[PromiseState]]
이 이를 담당하고 있음을 알 수 있습니다. 그러나 PromiseState
는 well-known symbol 이 아니므로 이 상태를 "당장"가져올 수 없는 비공개 필드로 취급할 수 있습니다.그러나
Promise.race
를 사용하여 현재 약속 상태를 테스트할 수 있습니다. 이를 위해 Promise.race
의 기능을 사용할 수 있습니다.📝 Promise.race는 순서대로 약속을 확인합니다. 예를 들어:
const a = Promise.resolve(1);
const b = Promise.resolve(2);
Promise.race([a, b]).then(console.log); // 1
당시:
const a = Promise.resolve(1);
const b = Promise.resolve(2);
Promise.race([b, a]).then(console.log); // 2
📝 이 코드는 약속 상태를 테스트합니다.
const pending = {
state: 'pending',
};
function getPromiseState(promise) {
// We put `pending` promise after the promise to test,
// which forces .race to test `promise` first
return Promise.race([promise, pending]).then(
(value) => {
if (value === pending) {
return value;
}
return {
state: 'resolved',
value
};
},
(reason) => ({ state: 'rejected', reason })
);
}
사용법https://codesandbox.io/s/restless-sun-njun1?file=/src/index.js
(async function () {
let result = await getPromiseState(Promise.resolve("resolved hello world"));
console.log(result);
result = await getPromiseState(Promise.reject("rejected hello world"));
console.log(result);
result = await getPromiseState(new Promise(() => {}));
console.log(result);
result = await getPromiseState("Hello world");
console.log(result);
})();
확인 외에도
Promise.race
의 이 기능은 시간 초과가 있는 일부 코드를 실행하는 데 유용할 수 있습니다. 예:const TIMEOUT = 5000;
const timeout = new Promise((_, reject) => setTimeout(() => reject('timeout'), TIMEOUT));
// We may want to test check if timeout is rejected
// before time consuming operations in the async code
// to cancel execution
async function someAsyncCode() {/*...*/}
const result = Promise.race([someAsyncCode(), timeout]);
자신만의 Promise Queue를 만드는 방법
때로는 서로 다른 코드 블록을 어떤 순서로 차례로 실행해야 합니다. 순차적으로 실행하려는 무거운 비동기 블록이 많을 때 유용합니다. 우리는 다음을 기억해야 합니다.
📝 JS는 단일 스레드이므로 동시 실행은 가능하지만 병렬은 아닙니다.
이를 위해 Promise Queue를 구현할 수 있습니다. 이 대기열은 이전에 추가된 모든 비동기 함수 뒤에 모든 함수를 넣습니다.
이 코드 블록을 확인해 보겠습니다.
class Queue {
// By default the queue is empty
_queue = Promise.resolve();
enqueue(fn) {
// Plan new operation in the queue
const result = this._queue.then(fn);
// avoid side effects.
// We can also provide an error handler as an improvement
this._queue = result.then(() => {}, () => {});
// To preserve promise approach, let's return the `fn` result
return result;
}
// If we want just to understand when the queue is over
wait() {
return this._queue;
}
}
테스트해 봅시다:
// Work emulator
const emulateWork = (name, time) => () => {
console.log(`Start ${name}`);
return new Promise((resolve) => {setTimeout(() => console.log(`End ${name}`) || resolve(), time)})
}
// Let's check if the queue works correctly if the promise fails
const failTest = () => () => {
console.log(`Start fail`);
return Promise.reject();
}
const queue = new Queue();
queue.enqueue(emulateWork('A', 500));
queue.enqueue(emulateWork('B', 500));
queue.enqueue(emulateWork('C', 900));
queue.enqueue(emulateWork('D', 1200));
queue.enqueue(emulateWork('E', 200));
queue.enqueue(failTest());
queue.enqueue(emulateWork('F', 900));
결과는 다음과 같습니다.
그러나 Promise가 거부되면 오류를 완전히 삼켰습니다.
즉, 실제 코드가 실패하면 우리는 그것에 대해 결코 알 수 없습니다.
📝 프라미스로 작업하는 경우 프라미스 체인의 끝에
.catch
를 사용해야 합니다. 그렇지 않으면 애플리케이션의 실패를 놓칠 수 있습니다!상황을 해결하기 위해 생성자에 대한 콜백을 제공할 수 있습니다.
class Queue {
_queue = Promise.resolve();
// By default onError is empty
_onError = () => {};
constructor(onError) {
this._onError = onError;
}
enqueue(fn) {
const result = this._queue.then(fn);
this._queue = result.then(() => {}, this._onError);
return result;
}
wait() {
return this._queue;
}
}
아니면 우리는 약속을 다시 무장시킬 수 있습니다!
class Queue {
_queue = Promise.resolve();
enqueue(fn) {
const result = this._queue.then(fn);
this._queue = result.then(() => {}, () => {});
// we changed return result to return result.then()
// to re-arm the promise
return result.then();
}
wait() {
return this._queue;
}
}
동일한 테스트에서 오류가 보고됩니다!
📝 모든 약속 체인은 unhandledRejection 오류를 일으킬 수 있습니다.
우리가 만든 코드를 실험해보고 싶다면: https://codesandbox.io/s/adoring-platform-cfygr5?file=/src/index.js
마무리
Promise는 복잡한 비동기 상호 작용을 해결해야 할 때 매우 유용합니다.
다음 기사에서는 JS Promise가 때때로
thenable
객체로 불리는 이유에 대해 설명하고 실험을 계속할 것입니다.
Reference
이 문제에 관하여(101 시리즈: 프라미스 #2: 현재 프라미스 상태를 얻고 자신만의 프라미스 큐를 만드는 방법?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/xnimorz/101-series-promises-2-how-to-get-current-promise-status-and-build-your-own-promise-queue-18j8텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)