Чи є спосіб зробити багатопотокове в JavaScript?
Чи є спосіб зробити багатопотокове в JavaScript?
Відповіді:
Дивіться http://caniuse.com/#search=worker для найновішої інформації про підтримку.
Далі був стан підтримки близько 2009 року.
Слова, для яких потрібно перейти в Google, - це Worker Threads
Окрім Gears, зараз немає нічого доступного, але є багато розмов про те, як це здійснити, тому я думаю, слідкуйте за цим питанням, оскільки відповідь, без сумніву, зміниться в майбутньому.
Ось відповідна документація для Gears: WorkerPool API
WHATWG має проект рекомендації для робочих тем: веб-робітники
А ще є робочі нитки Mozilla DOM
Оновлення: червень 2009 р., Поточний стан підтримки браузера для потоків JavaScript
У Firefox 3.5 працюють веб-працівники. Деякі демонстрації веб-працівників, якщо ви хочете бачити їх у дії:
Плагін Gears також можна встановити у Firefox.
У сафарі 4 та у сонечках WebKit є робочі нитки:
У Chrome задіяно Gears, тож він може робити нитки, хоча він вимагає від користувача запиту про підтвердження (і він використовує інший API для веб-працівників, хоча він працюватиме в будь-якому браузері зі встановленим плагіном Gears):
IE8 та IE9 можуть робити нитки лише із встановленим плагіном Gears
До HTML5 JavaScript дозволяв виконувати лише один потік на сторінці.
Був деякий Hacky спосіб імітації асинхронного виконання з Вихід , setTimeout()
, setInterval()
, XMLHttpRequest
або обробники подій (див в кінці цього поста для прикладу з виходом і setTimeout()
).
Але за допомогою HTML5 ми тепер можемо використовувати Worker Threads для паралельного виконання функцій. Ось приклад використання.
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>
Ми бачимо, що три потоки виконуються паралельно і друкують їх поточне значення на сторінці. Вони не заморожують сторінку, оскільки вони виконані у фоновому режимі з окремими потоками.
Ще один спосіб досягти цього - використовувати кілька кадрів 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);
}
})();
"Наївним" способом було б виконання функції 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, який відображається в хромі: // прапори / # enable-javascript-гармонія ).
Ключове слово урожай викликає призупинення виконання функції генератора, а значення виразу після ключового слова дохідності повертається на виклик генератора. Це можна розглядати як генератор версії ключового слова на основі генератора.
Генератор дозволяє призупинити виконання функції та відновити її пізніше. Генератор може бути використаний для планування ваших функцій за допомогою техніки, званої батут .
Ось приклад:
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>
З HTML5 "side-specs" більше не потрібно зламати javascript з setTimeout (), setInterval () тощо.
HTML5 & Friends представляє специфікацію веб-робітників javascript . Це API для запуску скриптів асинхронно та незалежно.
Посилання на специфікацію та навчальний посібник .
Немає справжньої нитки в JavaScript. JavaScript, який є ковкою мовою, якою вона є, дозволяє вам емулювати деякі з них. Ось приклад, на який я натрапив днями.
Ось лише спосіб імітувати багатопотоковість у Javascript
Зараз я збираюся створити 3 потоки, які будуть обчислювати додавання чисел, числа можна розділити на 13, а числа можна розділити з 3 до 10000000000. І ці 3 функції не в змозі запускатись одночасно з тим, що означає Concurrency. Але я покажу вам трюк, який змусить ці функції виконуватись рекурсивно за один і той же час: 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();
Сподіваюся, цей спосіб буде корисним.
Ви можете використовувати Narrative JavaScript , компілятор, який перетворює ваш код у стан машини, ефективно дозволяючи емулювати нитку. Це робиться, додаючи до "оперативного" оператора (позначеного як '->') до мови, який дозволяє записувати асинхронний код в єдиний лінійний блок коду.
Новий двигун v8, який повинен вийти сьогодні, підтримує його (я думаю)
У сирому Javascript найкраще, що ви можете зробити, - це використовувати декілька асинхронних викликів (xmlhttprequest), але це насправді не є нанизуванням і дуже обмеженим. Google Gears додає до браузера ряд API, деякі з яких можна використовувати для підтримки потоків.
Якщо ви не можете або не хочете використовувати будь-які речі AJAX, використовуйте iframe або десять! ;) Ви можете мати процеси, що працюють у iframes паралельно з основною сторінкою, не турбуючись про проблеми, пов’язані з перехресним веб-переглядачем, або проблеми з синтаксисом з точковою мережею AJAX тощо. Ви можете викликати JavaScript головної сторінки (включаючи JavaScript, який вона імпортувала) з iframe
Наприклад, у батьківському iframe для виклику egFunction()
в батьківському документі після завантаження вмісту iframe (це асинхронна частина)
parent.egFunction();
Динамічно генеруйте також iframes, щоб основний код HTML був вільним від них, якщо ви хочете.
Іншим можливим методом є використання інтерпретатора javascript у середовищі javascript.
Створюючи кілька перекладачів і контролюючи їх виконання з основної нитки, ви можете імітувати багатопотокові передачі з кожним потоком, що працює у власному середовищі.
Підхід дещо схожий з веб-працівниками, але ви даєте інтерпретатору доступ до глобального середовища браузера.
Я зробив невеликий проект, щоб продемонструвати це .
Більш детальне пояснення в цій публікації в блозі .
У JavaScript немає потоків, але у нас є працівники.
Робітники можуть бути хорошим вибором, якщо вам не потрібні спільні об’єкти.
Більшість реалізацій браузера фактично поширюють працівників по всіх ядрах, що дозволяє використовувати всі ядра. Демонстрацію цього ви можете побачити тут .
Я розробив бібліотеку під назвою task.js, що робить це дуже просто.
task.js Спрощений інтерфейс для отримання інтенсивного коду CPU для запуску на всіх ядрах (node.js та 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, є Web Workers, що JavaScript працює у фоновому режимі, незалежно від інших сценаріїв, не впливаючи на продуктивність сторінки.
Він підтримується майже у всіх браузерах:
Chrome - 4.0+
IE - 10,0+
Mozilla - 3,5+
Сафарі - 4.0+
Опера - 11,5+