考慮以下代碼javascript
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
。java
固然,服務器能夠驗證以後統統拒掉,可是用戶體驗不好。這是個很合理的需求,因此我特地在SF上提問,惋惜看起來並無現成的輪子能夠用。segmentfault
因此仍是隻能本身造。服務器
用下面的函數包裹原函數,若是前一次請求還沒有結束,新請求會和舊請求一塊兒返回。app
/** * 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; };
用下面的函數包裹原函數,若是前一次請求還沒有結束,新請求會排隊。dom
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; }
示例使用async
/** * 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受權。ide