【问题标题】:Javascript, how to write a Array.sort method so that it is called through a timeout strategy?Javascript,如何编写一个 Array.sort 方法以便通过超时策略调用它?
【发布时间】:2017-01-04 12:53:07
【问题描述】:

我的 sort() 函数遇到了瓶颈,例如:

list.sort(function (a,b) {return (a.value - b.value);});

冻结浏览器几秒钟。

对于具有循环的相同情况,建议使用“超时”策略,例如此处描述的策略:

How to stop intense Javascript loop from freezing the browser

那么问题来了,能不能用sort方法来实现呢?

*在评论讨论后编辑

// main_div is a div defined before
for (let i=0; i<list.length; i++) {
    main_div.appendChild(document.getElementById(list[i].id));
}

【问题讨论】:

  • 数组中有多少个元素?有没有可能使用web worker
  • @JaromandaX 我现在正在调查它,几分钟前发现的,所以我不知道。这些元素现在就像 +500,但它们可能/将会更多
  • 500 个元素将浏览器冻结 2 秒?你在 80386 上运行吗?
  • @JaromandaX 既然你这样说,可能冻结浏览器的原因是重新定位 DOM 节点而不是排序。但是,这个问题仍然相关,因为正如您所建议的那样,它涉及使用网络工作者。我正在查看html5rocks.com/en/tutorials/workers/basics 并尝试从stackoverflow.com/questions/3379875/… 之类的函数中获取 js_file,但到目前为止没有成功
  • @JaromandaX 但我也在运行 Chromebook,整个系统时不时会失去响应能力,因此排序也可能成为瓶颈

标签: javascript sorting freeze


【解决方案1】:

您可以使用本机 sort 方法执行排序,但在单独的线程中,使用 Web Worker。 Web Worker 将在完成其工作时发出通知。我已经将它包装在 ES6 承诺中,因此您可以使用 then 方法(请参阅下面的非承诺版本):

function asyncSort(data) {
    // Return a promise
    return new Promise(function (resolve) {
        // function to be called by web worker:
        function onmessage(e) {
            e.data.sort();
            postMessage(e.data);
        }
        // Create the worker, passing it the above code (as blob URL)
        var worker = new Worker(URL.createObjectURL(
            new Blob(['onmessage = ' + onmessage.toString()])));
        // Capture the event when worker has finished
        worker.onmessage = function (e) { 
            resolve(e.data); // resolve the promise
        };
        // Let worker execute the sort (async)
        worker.postMessage(data);
    });
}

// Sample call
asyncSort([2, 3, 1]).then(function (result) {
    console.log(result); // [1, 2, 3]
});

non-promise 版本如下所示:

function asyncSort(data, callback) {
    function onmessage(e) {
        e.data.sort();
        postMessage(e.data);
    }
    var worker = new Worker(URL.createObjectURL(
        new Blob(['onmessage = ' + onmessage.toString()])));
    worker.onmessage = function (e) { 
        callback(e.data);
    };
    worker.postMessage(data);
}

asyncSort([2, 3, 1], function (result) {
    console.log(result);
});

请注意,IE(至少到版本 11)会在 Blob URL 上引发安全错误。作为一种解决方法,您必须创建一个仅包含以下内容的单独 JS 脚本文件:

onmessage = function (e) {
    e.data.sort();
    postMessage(e.data);
}

...然后将该脚本引用为原始脚本中 Worker 的 URL:

new Worker('script.js')

【讨论】:

  • 是的!这就是我一直在寻找的,感谢让我看看如何用一个简单的函数定义一个新的工人。可惜网络工作者无法访问文档或 DOM :)
  • 不客气。 DOM 操作的另一个想法是:创建一个包含排序数据对和相关 DOM HTML(片段)的数组。按“排序数据”(无论您按什么排序)对其进行排序,然后将所有 HTML 连接在一起并将其插入回去。缺点是您需要重新分配事件处理程序。
  • 我所做的是用我的节点的 id 和我想要排序的数据生成一个数组,所以 sort() 只对这个数组进行排序,然后使用排序后的数组我可以访问我的通过 id 结点。我想这就是你在某种意义上说的(?)
  • 这是一个很好的解决方案,是的:在排序返回后,您将通过其 id 再次查找每个元素,然后将其移动到正确的位置。我实际上是在暗示提供节点的完整 HTML(而不是其 id),认为您可以一次性销毁所有元素并通过注入排序的 HTML 重新创建它们。在某些情况下,这可能会更快,但有事件处理的缺点。
【解决方案2】:

你可以实现自己的排序功能,例如这里简单的选择排序:

function asort(a, compare, done) {
    var i = 0, j = 0, len = a.length;

    function step() {
        if(j >= len)
            [i, j] = [i + 1, i + 1];
        if(i >= len)
            return done();
        if(compare(a[j], a[i]) < 0)
            [a[i], a[j]] = [a[j], a[i]]
        j++;
        setTimeout(step, 0);
    };

    step();
}

这比普通的sort慢很多,但不会阻塞主线程。

【讨论】:

  • 也许您应该添加一条说明,说明您使用的 ES2015+ 方法在 IE11 中不起作用 - 除非您有周年纪念更新(Edge 14),否则它在 Edge 中也不起作用
  • 感谢您的提醒……但老实说,我看不出这样的 cmets 有什么意义。如果您觉得应该在帖子中添加一些内容 - 只需对其进行编辑,这就是 SO 的工作方式。
  • 我只在 SO 上工作了一年,每次我发布葡萄酒在 ie7 上工作的答案时,我都会得到这样的建议。所以我认为这就是它在这里的工作方式。 IE。找到任何批评一个好答案的地方
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-04
  • 2021-12-16
  • 1970-01-01
  • 1970-01-01
  • 2011-07-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多