Javascript是单线程的,还会出现数据竞争吗?固然会!

考虑以下代码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-1step-2之间再次点击的话,就有可能同时发出两个step-1java

固然,服务器能够验证以后统统拒掉,可是用户体验不好。这是个很合理的需求,因此我特地在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

相关文章
相关标签/搜索