JavaScript и потоки

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

JavaScript не содержит потоков, по крайней мере, в его текущем виде. Что именно ты пытаешься сделать?

Eric Pohl 18.01.2011 01:27

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

Ludovic Feltz 24.02.2016 13:56
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
143
2
139 319
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

В необработанном Javascript лучшее, что вы можете сделать, - это использовать несколько асинхронных вызовов (xmlhttprequest), но это не совсем многопоточность и очень ограничена. Google Gears добавляет в браузер ряд API, некоторые из которых могут использоваться для поддержки потоковой передачи.

API Google Gears больше не доступен.

Ludovic Feltz 18.06.2015 11:22

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

Что вы имеете в виду под «истинным потоком»? Зеленые нити - настоящие нити.

Wes 15.01.2013 18:50
Ответ принят как подходящий

См. 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):

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

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

какие браузеры поддерживают Web Workers? Я знаю, что Firefox 3.5 делает ... и похоже, что эта функция была перенесена, и ее не будет в Chrome 2.

Jeff Atwood 26.06.2009 11:52

Хотя Safari 4 поддерживает веб-воркеров, похоже, что только Firefox поддерживает передачу сложных объектов через postMessage: hacks.mozilla.org/2009/07/working-smarter-not-harder См. Последний абзац этого сообщения об использовании в реальном мире в проекте Bespin, где приведены ссылки на прокладка для реализации Worker API с точки зрения Google Gears и добавляет недостающие функции в рабочую реализацию Safari 4, а также подробности о том, как они реализовали прозрачные настраиваемые события. поверх интерфейса postMessage.

Sam Hasler 09.07.2009 19:49

Теперь IE9 отсутствует, вы можете обновить «IE8 может выполнять потоки только с установленным плагином Gears» на «IE8 и IE9 могут выполнять потоки только с установленным плагином Gears»

BenoitParis 29.10.2010 12:15

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

Sam Hasler 03.09.2012 12:32

А где нужны «длительные вычисления» на html-странице?

inf3rno 03.09.2012 14:21

@ inf3rno: См. этот вопрос и ответ: Каковы варианты использования веб-воркеров?

Sam Hasler 03.09.2012 14:28

@SamHasler Вы можете пересмотреть свой ответ. Веб-воркеры теперь поддерживаются всеми современными настольными браузерами. См. Также caniuse.com/#search=worker

Rob W 06.04.2013 12:42

@SamHasler также стоит отметить, что Google Gears больше не поддерживается.

skeggse 04.02.2015 21:22

В 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-коде они не использовались, если хотите.

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

oligofren 24.01.2013 13:20

Вот только способ многопоточности моделировать в 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.

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

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

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

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

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

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

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

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


Настоящая многопоточность

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

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);
    }
})();

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

Однопоточный: имитируйте параллелизм 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, появляющийся в хром: // флаги / # включить-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>

Это действительно должен быть лучший ответ.

Jerry Liu 05.01.2017 15:34

В 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+

Другие вопросы по теме