Подтвердить что ты не робот

Выполнение фоновой задачи в Javascript

У меня есть интенсивная задача cpu, которую мне нужно запустить на клиенте. В идеале, я хотел бы иметь возможность вызывать функцию и запускать события прогресса с помощью jquery, чтобы я мог обновить интерфейс.

Я знаю, что javascript не поддерживает потоки, но я видел несколько многообещающих статей, пытающихся имитировать потоки с помощью setTimeout.

Каков наилучший подход для этого? Спасибо.

4b9b3361

Ответ 1

У меня возникла аналогичная проблема для решения недавно, когда мне нужно было поддерживать свободный поток пользовательского интерфейса, пока хруст некоторых данных для отображения.

Я написал библиотеку Background.js для обработки нескольких сценариев: последовательная фоновая очередь (на основе библиотеки WorkerQueue), список заданий, каждый из которых вызывается на каждый таймер, и итератор массива, чтобы помочь раскрыть работу в меньшие куски. Примеры и код здесь: https://github.com/kmalakoff/background

Наслаждайтесь!

Ответ 2

В принципе, вы хотите разделить операцию на части. Скажем, у вас есть 10 000 элементов, которые вы хотите обработать, сохраните их в списке, а затем обработайте небольшое количество из них с небольшой задержкой между каждым вызовом. Здесь вы можете использовать простую структуру:

function performTask(items, numToProcess, processItem) {
    var pos = 0;
    // This is run once for every numToProcess items.
    function iteration() {
        // Calculate last position.
        var j = Math.min(pos + numToProcess, items.length);
        // Start at current position and loop to last position.
        for (var i = pos; i < j; i++) {
            processItem(items, i);
        }
        // Increment current position.
        pos += numToProcess;
        // Only continue if there are more items to process.
        if (pos < items.length)
            setTimeout(iteration, 10); // Wait 10 ms to let the UI update.
    }
    iteration();
}

performTask(
    // A set of items.
    ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'],
    // Process two items every iteration.
    2,
    // Function that will do stuff to the items. Called once for every item. Gets
    // the array with items and the index of the current item (to prevent copying
    // values around which is unnecessary.)
    function (items, index) {
        // Do stuff with items[index]
        // This could also be inline in iteration for better performance.
    });

Также обратите внимание, что Google Gears имеет поддержку для работы в отдельном потоке. Firefox 3.5 также представил своих собственных работников, которые делают то же самое (хотя они следуют стандарт W3, в то время как Google Gears использует свои собственные методы.)

Ответ 3

Если вы можете использовать браузер, который будет использоваться, иначе вы уже знаете, что это новая версия Firefox, вы можете использовать новый WebWorkers из Mozilla. Это позволяет вам создавать новые потоки.

Ответ 4

В зависимости от ваших требований, вы можете легко избавиться, используя Gears. Gears поддерживает потоки, которые могут делать то, что вы хотите.

Как вы уже упоминали, setTimeout - это другой вариант. В зависимости от типа вашей задачи вы можете передать каждую итерацию цикла отдельному вызову setTimeout с некоторым интервалом между ними или вам может потребоваться разделить фрагменты вашего основного алгоритма на отдельные функции, которые можно вызывать один за другим в так же, как вы бы назвали каждую итерацию.

Ответ 5

Отличный ответ Кевин! Я написал что-то подобное несколько лет назад, хотя и менее изощренный. Исходный код здесь, если кто-то этого захочет:

http://www.leapbeyond.com/ric/jsUtils/TaskQueue.js

Все, что имеет метод run(), может быть поставлено в очередь как задача. Задачи могут переупорядочивать себя для выполнения работы в кусках. Вы можете приоритизировать задачи, добавлять/удалять их по желанию, приостанавливать/возобновлять всю очередь и т.д. Хорошо работает с асинхронными операциями - моим первоначальным использованием для этого было управление несколькими параллельными XMLHttpRequests.

Основное использование довольно просто:

var taskQueue = new TaskQueue();
taskQueue.schedule("alert('hello there')");

Комментарии заголовка в файле .js содержат более сложные примеры.

Ответ 6

Вот мое решение проблемы, если кому-то нужен простой экземпляр кода для копирования:

    var iterate = function (from, to, action, complete) {
        var i = from;
        var impl = function () {
            action(i);
            i++;
            if (i < to) setTimeout(impl, 1);
            else complete();
        };
        impl();
    };

Ответ 7

Моя самая сильная рекомендация - показать простой load.gif во время операции. Пользователь часто принимает некоторую продолжительность, если им было сказано, что это может занять некоторое время.

Ajaxload - Ajax для загрузки gif-генератора

Ответ 8

Это очень простой пример того, как создавать потоки в JavaScript. Обратите внимание, что вам нужно прервать ваши функции потока (инструкция yeld). Если вы хотите, вы можете использовать setTimeout вместо цикла while, чтобы периодически запускать планировщик. Также обратите внимание, что этот пример работает только с версией JavaScript версии 1.7+ (firefox 3+) вы можете попробовать его здесь: http://jslibs.googlecode.com/svn/trunk/jseval.html

//// thread definition
function Thread( name ) {

    for ( var i = 0; i < 5; i++ ) {

        Print(i+' ('+name+')');
        yield;
    }
}

//// thread management
var threads = [];

// thread creation
threads.push( new Thread('foo') );
threads.push( new Thread('bar') );

// scheduler
while (threads.length) {

    var thread = threads.shift();
    try {
        thread.next();
        threads.push(thread);
    } catch(ex if ex instanceof StopIteration) { }
}

Вывод:

0 (foo) 0 (bar) 1 (foo) 1 (bar) 2 (foo) 2 (bar) 3 (foo) 3 (bar) 4 (foo) 4 (bar) 

Ответ 9

Кажется, эта проблема была решена в самом node.
require child_process, который включен в nodejs 0.10.4