JavaScript и темы - PullRequest
       82

JavaScript и темы

124 голосов
/ 27 августа 2008

Есть ли способ сделать многопоточность в JavaScript?

Ответы [ 13 ]

103 голосов
/ 27 августа 2008

См. http://caniuse.com/#search=worker для получения самой последней информации о поддержке.

Следующим состоянием поддержки было около 2009 года.


Слова, которые вы хотите найти в Google: Рабочие темы JavaScript

Помимо Gears на данный момент ничего не доступно, но есть много разговоров о том, как это реализовать, поэтому я думаю, посмотрите на этот вопрос, поскольку ответ, без сомнения, изменится в будущем.

Вот соответствующая документация для Gears: WorkerPool API

WHATWG предлагает черновой вариант рекомендации для рабочих потоков: Web Workers

А также есть рабочие потоки DOM от Mozilla 1025 *


Обновление: Июнь 2009, текущее состояние поддержки браузером потоков JavaScript

Firefox 3.5 имеет веб-работников. Несколько демонстраций веб-работников, если вы хотите увидеть их в действии:

Плагин Gears также можно установить в Firefox.

Safari 4 и WebKit nightlies имеют рабочие потоки:

Chrome имеет встроенные Gears, поэтому он может создавать потоки, хотя для этого требуется запрос подтверждения от пользователя (и он использует другой API для веб-работников, хотя он будет работать в любом браузере с Плагин Gears установлен):

  • Демонстрация Google Gears WorkerPool (не очень хороший пример, поскольку он работает слишком быстро для тестирования в Chrome и Firefox, хотя IE запускает его достаточно медленно, чтобы блокировать взаимодействие)

IE8 и IE9 могут выполнять только потоки с установленным плагином Gears

66 голосов
/ 17 июня 2015

Другой способ сделать многопоточность и асинхронность в JavaScript

До HTML5 JavaScript допускал выполнение только одного потока на страницу.

Был какой-то хакерский способ симулировать асинхронное выполнение с помощью обработчиков событий Yield , setTimeout(), setInterval(), XMLHttpRequest или (см. В конце этого поста пример с yield и setTimeout()).

Но теперь в HTML5 мы можем использовать рабочие потоки для распараллеливания выполнения функций. Вот пример использования.


Реальная многопоточность

Многопоточность: рабочие потоки JavaScript

HTML5 представлены потоки Web Worker (см .: совместимость браузеров )
Примечание. IE9 и более ранние версии не поддерживают его.

Эти рабочие потоки являются потоками JavaScript, которые работают в фоновом режиме, не влияя на производительность страницы. Для получения дополнительной информации о Web Worker прочитайте документацию или этого руководства .

Вот простой пример с 3-мя потоками Web Worker, которые считают в MAX_VALUE и показывают текущее вычисленное значение на нашей странице:

//As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706
function getScriptPath(foo){ return window.URL.createObjectURL(new Blob([foo.toString().match(/^\s*function\s*\(\s*\)\s*\{(([\s\S](?!\}$))*[\s\S])/)[1]],{type:'text/javascript'})); }

var MAX_VALUE = 10000;

/*
 *	Here are the workers
 */
//Worker 1
var worker1 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
//We add a listener to the worker to get the response and show it in the page
worker1.addEventListener('message', function(e) {
  document.getElementById("result1").innerHTML = e.data;
}, false);


//Worker 2
var worker2 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker2.addEventListener('message', function(e) {
  document.getElementById("result2").innerHTML = e.data;
}, false);


//Worker 3
var worker3 = new Worker(getScriptPath(function(){
    self.addEventListener('message', function(e) {
        var value = 0;
        while(value <= e.data){
            self.postMessage(value);
            value++;
        }
    }, false);
}));
worker3.addEventListener('message', function(e) {
    document.getElementById("result3").innerHTML = e.data;
}, false);


// Start and send data to our worker.
worker1.postMessage(MAX_VALUE); 
worker2.postMessage(MAX_VALUE); 
worker3.postMessage(MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

Мы видим, что три потока выполняются в параллельном режиме, и печатаем их текущее значение на странице. Они не замораживают страницу, потому что они выполняются в фоновом режиме с разделенными потоками.


Многопоточность: с несколькими фреймами

Другой способ добиться этого - использовать несколько iframes , каждый из которых будет выполнять поток. Мы можем дать iframe некоторые параметры по URL, а iframe может связаться со своим родителем, чтобы получить результат и распечатать его обратно ( iframe должен быть в том же домене).

Этот пример работает не во всех браузерах! iframes обычно выполняется в том же потоке / процессе, что и главная страница (но Firefox и Chromium, кажется, обрабатывают это по-разному).

Поскольку фрагмент кода не поддерживает несколько файлов HTML, я просто предоставлю здесь разные коды:

index.html:

//The 3 iframes containing the code (take the thread id in param)
<iframe id="threadFrame1" src="thread.html?id=1"></iframe>
<iframe id="threadFrame2" src="thread.html?id=2"></iframe>
<iframe id="threadFrame3" src="thread.html?id=3"></iframe>

//Divs that shows the result
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>


<script>
    //This function is called by each iframe
    function threadResult(threadId, result) {
        document.getElementById("result" + threadId).innerHTML = result;
    }
</script>

thread.html:

//Get the parameters in the URL: http://stackoverflow.com/a/1099670/2576706
function getQueryParams(paramName) {
    var qs = document.location.search.split('+').join(' ');
    var params = {}, tokens, re = /[?&]?([^=]+)=([^&]*)/g;
    while (tokens = re.exec(qs)) {
        params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
    }
    return params[paramName];
}

//The thread code (get the id from the URL, we can pass other parameters as needed)
var MAX_VALUE = 100000;
(function thread() {
    var threadId = getQueryParams('id');
    for(var i=0; i<MAX_VALUE; i++){
        parent.threadResult(threadId, i);
    }
})();

Имитация многопоточности

Однопоточность: эмулировать параллелизм JavaScript с помощью setTimeout ()

«Наивным» способом было бы выполнение функции setTimeout() одна за другой, например:

setTimeout(function(){ /* Some tasks */ }, 0);
setTimeout(function(){ /* Some tasks */ }, 0);
[...]

Но этот метод не работает , потому что каждая задача будет выполняться одна за другой.

Мы можем симулировать асинхронное выполнение, вызывая функцию рекурсивно, как это:

var MAX_VALUE = 10000;

function thread1(value, maxValue){
    var me = this;
    document.getElementById("result1").innerHTML = value;
    value++;
  
    //Continue execution
    if(value<=maxValue)
        setTimeout(function () { me.thread1(value, maxValue); }, 0);
}

function thread2(value, maxValue){
    var me = this;
    document.getElementById("result2").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread2(value, maxValue); }, 0);
}

function thread3(value, maxValue){
    var me = this;
    document.getElementById("result3").innerHTML = value;
    value++;
	
    if(value<=maxValue)
        setTimeout(function () { me.thread3(value, maxValue); }, 0);
}

thread1(0, MAX_VALUE);
thread2(0, MAX_VALUE);
thread3(0, MAX_VALUE);
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>

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


Однопоточность: эмуляция параллелизма JavaScript с помощью yield

Выход - это новая функция в ECMAScript 6 , она работает только на самой старой версии Firefox и Chrome (в Chrome необходимо включить Экспериментальный JavaScript появляется в chrome: // flags / # enable-javascript-harmony ).

Ключевое слово yield приводит к приостановке выполнения функции генератора, и значение выражения, следующего за ключевым словом yield, возвращается вызывающей стороне генератора. Его можно рассматривать как генераторную версию ключевого слова return.

Генератор позволяет приостановить выполнение функции и возобновить ее позже. Генератор может быть использован для планирования ваших функций с помощью техники под названием trampolining .

Вот пример:

var MAX_VALUE = 10000;

Scheduler = {
	_tasks: [],
	add: function(func){
		this._tasks.push(func);
	},	
	start: function(){
		var tasks = this._tasks;
		var length = tasks.length;
		while(length>0){
			for(var i=0; i<length; i++){
				var res = tasks[i].next();
				if(res.done){
					tasks.splice(i, 1);
					length--;
					i--;
				}
			}
		}
	}	
}


function* updateUI(threadID, maxValue) {
  var value = 0;
  while(value<=maxValue){
	yield document.getElementById("result" + threadID).innerHTML = value;
	value++;
  }
}

Scheduler.add(updateUI(1, MAX_VALUE));
Scheduler.add(updateUI(2, MAX_VALUE));
Scheduler.add(updateUI(3, MAX_VALUE));

Scheduler.start()
<div id="result1"></div>
<div id="result2"></div>
<div id="result3"></div>
14 голосов
/ 24 августа 2011

С HTML5 "side-specs" больше не нужно взламывать javascript с помощью setTimeout (), setInterval () и т. Д.

HTML5 & Friends представляет спецификацию javascript Web Workers . Это API для асинхронного и независимого запуска скриптов.

Ссылки на спецификацию и учебник .

11 голосов
/ 27 августа 2008

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

10 голосов
/ 27 августа 2008

В Javascript нет настоящей многопоточности, но вы можете получить асинхронное поведение, используя setTimeout() и асинхронные запросы AJAX.

Что именно вы пытаетесь достичь?

7 голосов
/ 08 февраля 2014

Вот только способ имитировать многопоточность в Javascript

Теперь я собираюсь создать 3 потока, которые будут вычислять сложение чисел, числа можно разделить на 13 и числа можно разделить на 3 до 10000000000. И эти 3 функции не могут работать одновременно с тем, что означает параллелизм. Но я покажу вам трюк, который заставит эти функции работать рекурсивно в одно и то же время: jsFiddle

Этот код принадлежит мне.

Часть тела

    <div class="div1">
    <input type="button" value="start/stop" onclick="_thread1.control ? _thread1.stop() : _thread1.start();" /><span>Counting summation of numbers till 10000000000</span> = <span id="1">0</span>
</div>
<div class="div2">
    <input type="button" value="start/stop" onclick="_thread2.control ? _thread2.stop() : _thread2.start();" /><span>Counting numbers can be divided with 13 till 10000000000</span> = <span id="2">0</span>
</div>
<div class="div3">
    <input type="button" value="start/stop" onclick="_thread3.control ? _thread3.stop() : _thread3.start();" /><span>Counting numbers can be divided with 3 till 10000000000</span> = <span id="3">0</span>
</div>

Часть Javascript

var _thread1 = {//This is my thread as object
    control: false,//this is my control that will be used for start stop
    value: 0, //stores my result
    current: 0, //stores current number
    func: function () {   //this is my func that will run
        if (this.control) {      // checking for control to run
            if (this.current < 10000000000) {
                this.value += this.current;   
                document.getElementById("1").innerHTML = this.value;
                this.current++;
            }
        }
        setTimeout(function () {  // And here is the trick! setTimeout is a king that will help us simulate threading in javascript
            _thread1.func();    //You cannot use this.func() just try to call with your object name
        }, 0);
    },
    start: function () {
        this.control = true;   //start function
    },
    stop: function () {
        this.control = false;    //stop function
    },
    init: function () {
        setTimeout(function () {
            _thread1.func();    // the first call of our thread
        }, 0)
    }
};
var _thread2 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 13 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("2").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread2.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread2.func();
        }, 0)
    }
};
var _thread3 = {
    control: false,
    value: 0,
    current: 0,
    func: function () {
        if (this.control) {
            if (this.current % 3 == 0) {
                this.value++;
            }
            this.current++;
            document.getElementById("3").innerHTML = this.value;
        }
        setTimeout(function () {
            _thread3.func();
        }, 0);
    },
    start: function () {
        this.control = true;
    },
    stop: function () {
        this.control = false;
    },
    init: function () {
        setTimeout(function () {
            _thread3.func();
        }, 0)
    }
};

_thread1.init();
_thread2.init();
_thread3.init();

Я надеюсь, что этот путь будет полезным.

6 голосов
/ 27 августа 2008

Вы можете использовать Narrative JavaScript , компилятор, который преобразует ваш код в конечный автомат, эффективно позволяя вам эмулировать потоки. Это делается путем добавления оператора «выдачи» (обозначается как «->») к языку, который позволяет писать асинхронный код в одном блоке линейного кода.

4 голосов
/ 13 января 2015

Другой возможный метод - использование интерпретатора javascript в среде javascript.

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

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

Я сделал небольшой проект, чтобы продемонстрировал это .

Более подробное объяснение в этом блоге .

4 голосов
/ 02 сентября 2008

Новый движок v8, который должен выйти сегодня поддерживает его (я думаю)

3 голосов
/ 04 декабря 2012

Если вы не можете или не хотите использовать какие-либо материалы AJAX, используйте iframe или десять! ;) У вас могут быть процессы, запущенные в iframes параллельно с главной страницей, не беспокоясь о проблемах, сравнимых с кроссбраузерными или синтаксическими проблемами с точечной сетью AJAX и т. Д., И вы можете вызывать JavaScript главной страницы (включая импортированный ею JavaScript) из IFrame.

Eg, в родительском iframe, вызвать egFunction() в родительском документе после загрузки содержимого iframe (это асинхронная часть)

parent.egFunction();

Динамически генерируйте ифреймы, поэтому основной HTML-код свободен от них, если хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...