如何在"后台"中运行javascript函数/不冻结UI

kiv*_*all 8 javascript

我做了一个HTML表单,在很多不同的选项卡中有很多问题(来自数据库).然后用户在这些问题中给出答案.每次用户更改选项卡时,我的Javascript都会创建一个保存.问题是每次更改选项卡时我都必须遍历所有问题,并且每次冻结表单大约5秒钟.

我一直在寻找答案如何在后台运行我的保存功能.显然没有真正的方法在后台运行某些东西,很多人建议使用setTimeout();例如这一个如何让一组js函数在后台运行

但是这些例子都没有解释或考虑到即使我使用类似的东西setTimeout(saveFunction, 2000);也无法解决我的问题.在这种情况下,它只将它推迟2秒.

有没有办法解决这个问题?

Don*_*uck 20

您可以使用网络工作者。这里的一些旧答案说它们没有得到广泛支持(我猜在编写这些答案时它们还没有得到广泛支持),但今天它们受到所有主要浏览器的支持

要运行 Web Worker,您需要创建内置Worker类的实例。构造函数采用一个参数,该参数是包含要在后台运行的代码的 javascript 文件的 URI。例如:

let worker = new Worker("/path/to/script.js");
Run Code Online (Sandbox Code Playgroud)

Web Worker 受同源策略的约束,因此如果您传递这样的路径,目标脚本必须与调用它的页面位于同一域中。

如果您不想为此创建新的 Javascript 文件,您还可以使用数据 URI:

let worker = new Worker(
    `data:text/javascript,
    //Enter Javascript code here
    `
);
Run Code Online (Sandbox Code Playgroud)

由于同源策略,您无法从数据 URI 发送 AJAX 请求,因此如果您需要在 Web Worker 中发送 AJAX 请求,则必须使用单独的 Javascript 文件。

您指定的代码(在单独的文件中或在数据 URI 中)将在您调用Worker构造函数后立即运行。

不幸的是,Web Worker 既无法访问外部 Javascript 变量、函数或类,也无法访问 DOM,但您可以通过使用方法postMessageonmessage事件来解决这个问题。在外部代码中,这些是工作对象的成员(worker在上面的示例中),而在工作人员内部,这些是全局上下文的成员(因此可以通过使用来调用它们this,或者就像前面没有任何内容一样)。

postMessage并且onmessage双向工作,因此当worker.postMessage在外部代码中调用时,onmessage会在工作线程中触发,而当postMessage在工作线程中调用时,worker.onmessage会在外部代码中触发。

postMessage接受一个参数,这是您想要传递的变量(但您可以通过传递数组来传递多个变量)。不幸的是,函数和 DOM 元素无法传递,当你尝试传递一个对象时,只会传递它的属性,而不是它的方法。

onmessage接受一个参数,该参数是一个MessageEvent对象。该MessageEvent对象有一个data属性,其中包含使用第一个参数发送的数据postMessage

这是使用网络工作者的示例。在此示例中,我们有一个函数 ,functionThatTakesLongTime它接受一个参数并根据该参数返回一个值,并且我们希望使用 Web Workers 以便在functionThatTakesLongTime(foo)不冻结 UI 的情况下查找foo外部代码中的某个变量在哪里。

let worker = new Worker(
    `data:text/javascript,
    function functionThatTakesLongTime(someArgument){
        //There are obviously faster ways to do this, I made this function slow on purpose just for the example.
        for(let i = 0; i < 1000000000; i++){
            someArgument++;
        }
        return someArgument;
    }
    onmessage = function(event){    //This will be called when worker.postMessage is called in the outside code.
        let foo = event.data;    //Get the argument that was passed from the outside code, in this case foo.
        let result = functionThatTakesLongTime(foo);    //Find the result. This will take long time but it doesn't matter since it's called in the worker.
        postMessage(result);    //Send the result to the outside code.
    };
    `
);

worker.onmessage = function(event){    //Get the result from the worker. This code will be called when postMessage is called in the worker.
    alert("The result is " + event.data);
}

worker.postMessage(foo);    //Send foo to the worker (here foo is just some variable that was defined somewhere previously).
Run Code Online (Sandbox Code Playgroud)


T.J*_*der 7

显然没有真正的方法在背景上运行某些东西......

大多数现代浏览器上(但不是IE9和更早版本):Web Workers.

但我认为你正在试图解决在错误级别的问题:1.应该在一个可以遍历所有控件的很多不到五秒钟,2.它不应该是必要通过所有循环只控制其中一个更改时控制.

我建议在尝试将处理卸载到后台之前查看这些问题.

例如,您可以拥有一个包含每个项目当前值的对象,然后让每个项目的UI在值更改时更新该对象.然后,您将拥有该对象中的所有值,而无需再次遍历所有控件.

  • 除了 Web Worker 之外,浏览器中的 Javascript 是单线程的。任何时候只要有长时间运行的代码,就会阻止与页面的交互。 (2认同)