斟酌以下代碼
whatever.onclick = async () => {
const a = await(await fetch('step-1')).text();
const b = await(await fetch('step-2')).text();
whatever.textContent = a + b;
}
假如用戶在step-1
和step-2
之間再次點擊的話,就有能夠同時發出兩個step-1
。
固然,服務器能夠考證以後統統拒掉,然則用戶體驗很差。這是個很合理的需求,所以我特意在SF上發問,惋惜看起來並沒有現成的輪子能夠用。
所以照樣只能本身造。
用下面的函數包裹原函數,假如前一次要求還沒有完畢,新要求會和舊要求一同返回。
/**
* Creates a function that invokes `originalFunction`, with the `this` binding
* and `arguments` of the created function, while there is no other pending
* excutions of `originalFunction`. Simultaneous calls to the created function
* return the result of the first pending `originalFunction` invocation.
*
* @param {function} originalFunction async function to wrap
*/
const debounceAsync = originalFunction => {
let currentExcution = null;
const wrappedFunction = async function () {
// 1. locked => return lock
if (currentExcution) return currentExcution;
// 2. released => apply
currentExcution = originalFunction.apply(this, arguments);
try {
return await currentExcution;
}
finally {
currentExcution = null;
}
};
return wrappedFunction;
};
用下面的函數包裹原函數,假如前一次要求還沒有完畢,新要求會列隊。
const endOfQueue = Promise.resolve();
const overrideResult = async lastExcution => {
try {
await lastExcution;
}
finally {
return endOfQueue;
}
}
/**
* Creates a function that invokes `originalFunction`, with the `this` binding
* and `arguments` of the created function, while there is no other pending
* excutions of `originalFunction`. Simultaneous calls to the created function
* will be queued up.
*
* @param {function} originalFunction async function to wrap
*/
const queueAsync = originalFunction => {
let lastExcution = endOfQueue;
const wrappedFunction = async function () {
// 1. queue up
const myExcution = lastExcution.then(() => originalFunction.apply(this, arguments));
// 2. update queue tail + swipe excution result from queue
lastExcution = overrideResult(myExcution);
// 3. return excution result
return myExcution;
};
return wrappedFunction;
}
示例運用
/**
* A promisified settimeout
*
* @param {number} [ms=0] time to sleep in ms
*/
const sleep = (ms = 0) => new Promise(resolve => setTimeout(resolve, ms));
const debounceAsync_UNIT_TEST = async () => {
const goodnight = debounceAsync(sleep);
for (let i = 0; i < 8; i++) {
goodnight(5000).then(() => console.log(Date()));
await sleep(500);
}
console.warn('Expected output: 8 identical datetime');
};
const queueAsync_UNIT_TEST = () => {
const badnight = queueAsync(i => sleep(i).then(() => { if (Math.random() > 0.5) throw new Error('uncaught error test: you should expect a console error message.') }));
badnight(1000);
badnight(1000);
badnight(1000);
badnight(1000);
badnight(1000).finally(() => console.log('5s!'));
badnight(1000);
badnight(1000);
badnight(1000);
badnight(1000);
badnight(1000).finally(() => console.log('10s!'));
console.warn('Check message timestamps.');
console.warn('Bad:');
console.warn('1 1 1 1 1:5s');
console.warn(' 1 1 1 1 1:10s');
console.warn('Good:');
console.warn('1 1 1 1 1:5s');
console.warn(' 1 1 1 1 1:10s');
}
以上一切代碼按Mozilla Public License, v. 2.0受權。
以上一切筆墨內容按CC BY-NC-ND 4.0受權。