Есть ли способ сделать многопоточность в JavaScript?
Можете ли вы изменить принятый ответ на этот? stackoverflow.com/a/30891727/2576706 Он намного больше разработан для будущего пользователя ..



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


В необработанном Javascript лучшее, что вы можете сделать, - это использовать несколько асинхронных вызовов (xmlhttprequest), но это не совсем многопоточность и очень ограничена. Google Gears добавляет в браузер ряд API, некоторые из которых могут использоваться для поддержки потоковой передачи.
API Google Gears больше не доступен.
В JavaScript нет настоящей многопоточности. JavaScript, будучи гибким языком, действительно позволяет имитировать некоторые из них. Вот пример, с которым я столкнулся на днях.
Что вы имеете в виду под «истинным потоком»? Зеленые нити - настоящие нити.
См. http://caniuse.com/#search=worker для получения самой последней информации о поддержке.
Следующее - состояние поддержки примерно в 2009 году.
Слова, которые вы хотите найти в Google, - это Рабочие потоки JavaScript.
Кроме Шестерни в настоящее время ничего не доступно, но есть много разговоров о том, как это реализовать, поэтому я думаю, следите за этим вопросом, поскольку ответ, несомненно, изменится в будущем.
Вот соответствующая документация для Gears: WorkerPool API
WHATWG имеет проект рекомендации для рабочих потоков: Веб-воркеры
И еще есть Mozilla Рабочие потоки DOM
Обновлять: Июнь 2009 г., текущее состояние поддержки браузером потоков JavaScript.
Firefox 3.5 имеет веб-воркеров. Некоторые демонстрации веб-воркеров, если вы хотите увидеть их в действии:
Плагин Gears также можно установить в Firefox.
Safari 4 и WebKit ночные игры имеют рабочие потоки:
Хром имеет встроенный Gears, поэтому он может выполнять потоки, хотя для этого требуется запрос подтверждения от пользователя (и он использует другой API для веб-воркеров, хотя он будет работать в любом браузере с установленным плагином Gears):
IE8 и IE9 могут выполнять потоки только с установленным плагином Gears
какие браузеры поддерживают Web Workers? Я знаю, что Firefox 3.5 делает ... и похоже, что эта функция была перенесена, и ее не будет в Chrome 2.
Хотя Safari 4 поддерживает веб-воркеров, похоже, что только Firefox поддерживает передачу сложных объектов через postMessage: hacks.mozilla.org/2009/07/working-smarter-not-harder См. Последний абзац этого сообщения об использовании в реальном мире в проекте Bespin, где приведены ссылки на прокладка для реализации Worker API с точки зрения Google Gears и добавляет недостающие функции в рабочую реализацию Safari 4, а также подробности о том, как они реализовали прозрачные настраиваемые события. поверх интерфейса postMessage.
Теперь IE9 отсутствует, вы можете обновить «IE8 может выполнять потоки только с установленным плагином Gears» на «IE8 и IE9 могут выполнять потоки только с установленным плагином Gears»
@ inf3rno для выполнения длительных вычислений в другом потоке, чтобы они не замедляли пользовательский интерфейс браузера.
А где нужны «длительные вычисления» на html-странице?
@ inf3rno: См. этот вопрос и ответ: Каковы варианты использования веб-воркеров?
@SamHasler Вы можете пересмотреть свой ответ. Веб-воркеры теперь поддерживаются всеми современными настольными браузерами. См. Также caniuse.com/#search=worker
@SamHasler также стоит отметить, что Google Gears больше не поддерживается.
В Javascript нет настоящей многопоточности, но вы можете получить асинхронное поведение, используя setTimeout() и асинхронные запросы AJAX.
Чего именно вы пытаетесь достичь?
Вы можете использовать Повествовательный JavaScript, компилятор, который преобразует ваш код в конечный автомат, эффективно позволяя эмулировать многопоточность. Это достигается путем добавления к языку «уступающего» оператора (обозначенного как '->'), который позволяет писать асинхронный код в одном линейном блоке кода.
Новый движок v8, который сегодня поддерживает должен выйти (я думаю)
С "боковыми спецификациями" HTML5 больше не нужно взламывать javascript с setTimeout (), setInterval () и т. д.
HTML5 & Friends представляет спецификацию javascript Веб-воркеры. Это API для асинхронного и независимого запуска скриптов.
Ссылки на Технические характеристики и руководство.
Если вы не можете или не хотите использовать какие-либо материалы AJAX, используйте iframe или десять! ;) Вы можете запускать процессы в iframe параллельно с главной страницей, не беспокоясь о проблемах, сопоставимых с кросс-браузером, или проблемах синтаксиса с dot net AJAX и т. д., И вы можете вызывать JavaScript главной страницы (включая JavaScript, который он импортировал) из iframe.
Например, в родительском iframe для вызова egFunction() в родительском документе после загрузки содержимого iframe (это асинхронная часть)
parent.egFunction();
Также динамически генерируйте фреймы, чтобы в основном HTML-коде они не использовались, если хотите.
На мой взгляд, это описание было слишком кратким. Не могли бы вы подробнее рассказать, как использовать эту технику, или опубликовать ссылку на учебное пособие, показывающее некоторый код?
Вот только способ многопоточности моделировать в 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();
Надеюсь, этот способ будет вам полезен.
Другой возможный метод - использование интерпретатора javascript в среде javascript.
Создав несколько интерпретаторов и контролируя их выполнение из основного потока, вы можете имитировать многопоточность, при которой каждый поток работает в своей собственной среде.
Подход чем-то похож на веб-воркеров, но вы предоставляете интерпретатору доступ к глобальной среде браузера.
Я сделал небольшой проект на продемонстрировать это.
Более подробное объяснение в это сообщение в блоге.
До HTML5 JavaScript разрешал выполнение только одного потока на странице.
Был какой-то хитрый способ имитировать асинхронное выполнение с помощью Урожай, setTimeout(), setInterval(), XMLHttpRequest или обработчики событий (см. В конце этого сообщения пример с урожай и setTimeout()).
Но с HTML5 теперь мы можем использовать рабочие потоки для распараллеливания выполнения функций. Вот пример использования.
HTML5 представил потоки веб-рабочих (см .: совместимость с браузерами)
Примечание. IE9 и более ранние версии не поддерживают его.
Эти рабочие потоки представляют собой потоки JavaScript, которые выполняются в фоновом режиме, не влияя на производительность страницы. Для получения дополнительной информации о Веб-воркерпрочтите документацию или этот учебник.
Вот простой пример с 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>Мы видим, что три потока выполняются в параллельном режиме и печатают свое текущее значение на странице. Они не замораживают страницу, потому что выполняются в фоновом режиме с разделенными потоками.
Другой способ добиться этого - использовать несколько iframe, каждый из которых будет выполнять поток. Мы можем передать iframe некоторые параметры по URL-адресу, и iframe может связаться со своим родителем, чтобы получить результат и распечатать его обратно (iframe должен находиться в том же домене).
Этот пример работает не во всех браузерах!iframe обычно выполняется в том же потоке / процессе, что и главная страница (но 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);
}
})();
«Наивным» способом было бы выполнить функцию 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>Как видите, этот второй метод очень медленный и приводит к зависанию браузера, поскольку он использует основной поток для выполнения функций.
Урожай - это новая функция в ECMAScript 6, она работает только в самых старых версиях Firefox и Chrome (в Chrome вам нужно включить Экспериментальный JavaScript, появляющийся в хром: // флаги / # включить-javascript-гармонию).
The yield keyword causes generator function execution to pause and the value of the expression following the yield keyword is returned to the generator's caller. It can be thought of as a generator-based version of the return keyword.
Генератор позволяет приостановить выполнение функции и возобновить его позже. Генератор можно использовать для планирования ваших функций с помощью техники, называемой прыжки на батуте.
Вот пример:
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>Это действительно должен быть лучший ответ.
В Javascript нет потоков, но у нас есть рабочие.
Рабочие могут быть хорошим выбором, если вам не нужны общие объекты.
Большинство реализаций браузеров фактически распределяют рабочих по всем ядрам, что позволяет использовать все ядра. Вы можете увидеть демонстрацию этого здесь.
Я разработал библиотеку под названием task.js, которая упрощает это.
task.js Simplified interface for getting CPU intensive code to run on all cores (node.js, and web)
Примером может быть
function blocking (exampleArgument) {
// block thread
}
// turn blocking pure function into a worker task
const blockingAsync = task.wrap(blocking);
// run task on a autoscaling worker pool
blockingAsync('exampleArgumentValue').then(result => {
// do something with result
});
С HTML5 Технические характеристики вам не нужно писать слишком много JS для того же или искать какие-то хаки.
Одна из функций, представленных в HTML5, - это Веб-воркеры, который представляет собой JavaScript, работающий в фоновом режиме, независимо от других скриптов, не влияющий на производительность страницы.
Поддерживается практически во всех браузерах:
Chrome - 4.0+
IE - 10.0+
Mozilla - 3.5+
Safari - 4.0+
Opera - 11.5+
JavaScript не содержит потоков, по крайней мере, в его текущем виде. Что именно ты пытаешься сделать?