英文 | https://betterprogramming.pub/5-javascript-promises-util-functions-to-spice-up-your-apps-665affca236c
翻譯 | 小愛
1、 模拟延遲
有時我們需要模拟某些動作之間的特定延遲。使用以下代碼就很容易實作:
function delay(timeout) {
return new Promise(
(resolve) => {
const timeoutHandle =
setTimeout(() => {
clearTimeout(timeoutHandle);
resolve()
}, timeout);
});
}
這個util函數的用法如下:
async function(){
console.log('The first log');
await delay(1000);
console.log('The second log with 1000 ms delay')
}
作為輸出,我們将立即看到第一個日志,第二個日志在 1000 毫秒後出現。
2、分解長期運作的任務
由于 JS 是一種單線程語言,我們可能會遇到 UI 延遲或無響應的伺服器來處理新的即将到來的請求。它通常發生在應用程式試圖處理長時間運作的任務時。
有時,需要一個工具完成将長時間運作的任務進行拆分為多個塊,以便有機會完成另一個應用程式代碼。這是代碼:
function nextFrame() {
const nextTick = requestAnimationFrame || setImmediate.
return new Promise((res) => nextTick(() => res()))
}
這個util函數的用法:
async longRunningTask(){
let step = 0;
while(true){
if (++step % 5 === 0){
await nextFrame();
}
}
}
longRunningTask();
console.log('The first log')
盡管我們運作了一個無限循環,但這個函數的第 5 步為處理其他應用程式的代碼開辟了道路。
3、為 Promise 添加逾時限制
如果某些操作正在使用 Promise 處理,那麼使用以下代碼很容易為其添加逾時限制:
function addTimeoutToPromise(targetPromise, timeout) {
let timeoutHandle;
const timeoutLimitPromise = new Promise((res, rej) => {
timeoutHandle = setTimeout(
() => rej(new Error('Timeout exceeded')),
timeout
);
});
return Promise.race([targetPromise, timeoutLimitPromise])
.then((res) => {
clearTimeout(timeoutHandle);
return res;
});
}
這個util函數的用法如下圖所示:
addTimeoutToPromise(
delay(1000).then(() => console.log('Completed')), 2000
);
// --> Completed
addTimeoutToPromise(
delay(2000), 1000
).catch((e) => console.error(e.message))
// --> Timeout exceeded
4、按順序完成 Promise
假設我們有一個 API,它對可以同時完成的請求數量有限制。它迫使我們開發一種方法,允許一個接一個地完成一堆承諾。
首先,與 Promise.all 不同,我們不能隻将一個 promise 數組傳遞給我們的方法,因為一旦建立了 promise,所有這些都會立即完成。
是以,我們的 util 函數應該獲得一組函數,這些函數可以按需建立承諾,我們可以控制這個承諾何時開始競争,如下所示:
function completeInSequence(promiseFactories: () => Promise<any>){
...
}
從字面上看,我們需要建構以下結構,它允許按順序完成承諾。下面是一些代碼來做到這一點:
Promise.resolve()
.then(() => promiseFacrories[0]()
.then(() => promiseFactories[1]())
...
.then(() => promiseFactories[N]())
在這裡, Promise.resolve 和 Array.reduce 發揮作用。Promise.resolve 通常用作建構承諾鍊的起點。這是幫助解決此問題的代碼:
function completeInSequence(promiseFactories: () => Promise<any>) {
return promiseFactories.reduce(
(chain, promiseFactory) => chain.then(()=> promiseFactory()),
Promise.resolve()
);
}
很簡單的實作。讓我們用以下内容測試它:
completeInSequence([
() => delay(1000).then(() => console.log('1')),
() => delay(1000).then(() => console.log('2')),
() => delay(1000).then(() => console.log('3'))
])
你将看到所有日志消息之間的間隔為 1000 毫秒。
5、隻同時完成 N 個 Promise
我們考慮到使用 API 的帶寬是很酷的,但我認為它可能允許我們同時完成多個請求。
好吧,和前面的例子一樣,我們仍然需要一個建立 promise 的函數,它提供了控制 promise 何時完成的機會。第二個變量是池的最大大小,它反映了可以同時處理的承諾數量。下面是一個例子:
function completePromisesInPool(
promiseFactories: () => Promise<any>,
maxPoolSize: number
) { .... }
首先,函數應該在我們需要開始完成N個promise的地方傳回Promise。
之後,隻要任何一個運作承諾完成,就意味着我們在池中有一個空閑插槽,如果它仍然存在,另一個承諾可能會取代這個位置,否則解決包裝承諾。這是執行此操作的函數:
function completePromisesInPool(
promiseFactories: () => Promise<any>,
maxPoolSize: number
) {
return new Promise((res) => {
let nextPromise = 0;
const runPromise = () => {
nextPromise++;
if (nextPromise > promiseFactories.length) {
res();
return;
}
return promiseFactories[nextPromise-1]()
.then(() => runPromise())
}
Array.from({length: maxPoolSize})
.map(() => runPromise());
})
}
讓我們使用以下内容對其進行測試:
completePromisesInPool([
() => delay(1000).then(() => console.log('1')),
() => delay(1000).then(() => console.log('2')),
() => delay(1000).then(() => console.log('3')),
() => delay(1000).then(() => console.log('4')),
() => delay(1000).then(() => console.log('5')),
() => delay(1000).then(() => console.log('6'))
], 2)
你将看到 console.log 以 1000 毫秒的間隔分批列印兩條消息。這是它的樣子:
就是這個樣子。
感謝你的閱讀。
學習更多技能
請點選下方公衆号