考虑如下代码
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授权。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。