资讯专栏INFORMATION COLUMN

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

Awbeci / 1958人阅读

摘要:考虑如下代码如果用户在和之间再次点击的话,就有可能同时发出两个。这是个很合理的需求,所以我特意在上提问,可惜看起来并没有现成的轮子可以用。用下面的函数包裹原函数,如果前一次请求尚未结束,新请求会排队。示例使用以上所有代码按授权。

考虑如下代码

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-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授权。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95456.html

相关文章

  • 浏览器知识

    摘要:浏览器的渲染进程是多线程的。异步请求线程在在连接后是通过浏览器新开一个线程请求将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。 [TOC] 浏览器进程线程 区分线程和进程 **- 什么是进程** 狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being exe...

    Pluser 评论0 收藏0
  • JS高级入门教程

    摘要:解析首先简称是由欧洲计算机制造商协会制定的标准化脚本程序设计语言。级在年月份成为的提议,由核心与两个模块组成。通过引入统一方式载入和保存文档和文档验证方法对进行进一步扩展。其中表示的标记位正好是低三位都是。但提案被拒绝了。 JS高级入门教程 目录 本文章定位及介绍 JavaScript与ECMAScript的关系 DOM的本质及DOM级介绍 JS代码特性 基本类型与引用类型 JS的垃...

    zsy888 评论0 收藏0
  • JS与Node.js中事件循环

    摘要:的单线程,与它的用途有关。特点的显著特点异步机制事件驱动。队列的读取轮询线程,事件的消费者,的主角。它将不同的任务分配给不同的线程,形成一个事件循环,以异步的方式将任务的执行结果返回给引擎。 这两天跟同事同事讨论遇到的一个问题,js中的event loop,引出了chrome与node中运行具有setTimeout和Promise的程序时候执行结果不一样的问题,从而引出了Nodejs的...

    abson 评论0 收藏0
  • 第五天 JavaScript线程详解

    摘要:若以多线程的方式操作这些,则可能出现操作的冲突。另外,因为是单线程的,在某一时刻内只能执行特定的一个任务,并且会阻塞其它任务执行。浏览器事件触发线程事件触发线程,当一个事件被触发时该线程会把事件添加到任务队列的队尾,等待引擎的处理。 首先,说下为什么 JavaScript 是单线程? 总所周知,JavaScript是以单线程的方式运行的。说到线程就自然联想到进程。那它们有什么联系呢? ...

    caiyongji 评论0 收藏0
  • 细说JavaScript线程一些事

    摘要:标签单线程首发地址码农网细说单线程的一些事最近被同学问道单线程的一些事,我竟回答不上。若以多线程的方式操作这些,则可能出现操作的冲突。另外,因为是单线程的,在某一时刻内只能执行特定的一个任务,并且会阻塞其它任务执行。 标签: JavaScript 单线程 首发地址:码农网《细说JavaScript单线程的一些事》 最近被同学问道 JavaScript 单线程的一些事,我竟回答不上。好...

    sarva 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<