Promise의 성질을 이용하여 다양한 응용을 할 수 있다.
시간초과 추가하기
1 2 3 4 5 6 7 8 9 10
| const awaitTimeout = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
awaitTimeout(300).then(() => console.log("Hi"));
const f = async () => { await awaitTimeout(300); console.log("Hi"); };
|
위의 코드는 delay를 받아서 delay 뒤에 resolve 하는 Promise로 래핑한 함수이다.
Promise.race
를 이용하여 시간초과 로직을 추가할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const awaitTimeout = (delay, reason) => new Promise( (resolve, reject) => setTimeout(() => (reason === undefined ? resolve() : reject(reason))), delay );
const wrapPromise = (promise, delay, reason) => Promise.race([promise, awaitTimeout(delay, reason)]);
wrapPromise(fetch("https://cool.api.io/data.json"), 3000, { reason: "Fetch timeout", }) .then((data) => { console.log(data.message); }) .catch((data) => console.log(`Failed with reason: ${data.reason}`));
|
promise.race
는 가장 먼저 resolve된 데이터만을 사용하는 함수이다.
이러한 성질을 이용하여 delay안에 promise가 resolve되지 못할 시 에러를 발생시킨다.
시간초과를 위한 함수들을 하나의 객체로 정의하였다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| class Timeout { constructor() { this.ids = []; }
set = (delay, reason) => new Promise((resolve, reject) => { const id = setTimeout(() => { if (reason === undefined) resolve(); else reject(reason); this.clear(id); }, delay); this.ids.push(id); });
wrap = (promise, delay, reason) => Promise.race([promise, this.set(delay, reason)]);
clear = (...ids) => { this.ids = this.ids.filter((id) => { if (ids.includes(id)) { clearTimeout(id); return false; } return true; }); }; }
const myFunc = async () => { const timeout = new Timeout(); const timeout2 = new Timeout(); timeout.set(6000).then(() => console.log("Hello")); timeout2.set(4000).then(() => console.log("Hi")); timeout .wrap(fetch("https://cool.api.io/data.json"), 3000, { reason: "Fetch timeout", }) .then((data) => { console.log(data.message); }) .catch((data) => console.log(`Failed with reason: ${data.reason}`)) .finally(() => timeout.clear(...timeout.ids)); };
|
더욱 더 독립적으로 사용할 수 있게 되었다.
디바운싱
클로저와 Promise를 이용하여 디바운싱을 구현할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| const debouncePromise = (fn, ms = 0) => { let timeoutId; const pending = []; return (...args) => new Promise((res, rej) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => { const currentPending = [...pending]; pending.length = 0; Promise.resolve(fn.apply(this, args)).then( (data) => { currentPending.forEach(({ resolve }) => resolve(data)); }, (error) => { currentPending.forEach(({ reject }) => reject(error)); } ); }, ms); pending.push({ resolve: res, reject: rej }); }); };
const fn = (arg) => new Promise((resolve) => { setTimeout(resolve, 1000, ["resolved", arg]); }); const debounced = debouncePromise(fn, 200); debounced("foo").then(console.log); debounced("bar").then(console.log);
|
위 코드는 ms 만큼의 delay 후에 마지막 호출의 값을 반환한다. 이전에 호출된 함수들은 마지막 호출과 동일한 데이터를 반환한다.
호출될 경우 외부 pending 배열을 복사한 후 이를 초기화한다.
ms동안 새로운 호출이 없을 경우 복사한 pending 배열을 순회하며 resolve나 reject를 호출한다.
Ref
How can I add a timeout to a promise in JavaScript?
Debounce promise
Promise
UsingPromise
debounce function implemented with promises