Как JavaScript обрабатывает ответы AJAX в фоновом режиме?

141

Поскольку JavaScript выполняется в одном потоке, что на самом деле происходит в фоновом режиме после выполнения запроса AJAX? Я хотел бы получить более глубокое представление об этом, может ли кто-нибудь пролить свет?

Азиз Пунджани
источник
6
Довольно хорошее описание здесь: stackoverflow.com/questions/2914161/ajax-multi-threaded
Джонатан М.
3
Код JavaScript является однопоточным (за исключением веб-воркеров), но не браузер, на котором запущен движок JavaScript ...
Хуан Мендес,
@JuanMendes Выполняется ли JavaScript в одном потоке, а очередь событий - в другом?
Шон Латтин,
1
@ShaunLuttin Нет, очередь событий запускает JavaScript
Хуан Мендес

Ответы:

217

Под обложками у javascript есть очередь событий. Каждый раз, когда поток выполнения javascript завершается, он проверяет, есть ли в очереди другое событие для обработки. Если есть, он извлекает его из очереди и запускает это событие (например, щелчок мышью).

Сеть на собственном коде, которая находится под вызовом ajax, будет знать, когда ответ ajax будет выполнен, и событие будет добавлено в очередь событий javascript. Как собственный код узнает, когда выполняется вызов ajax, зависит от реализации. Он может быть реализован с помощью потоков или также может управляться событиями (на самом деле это не имеет значения). Суть реализации заключается в том, что когда ответ ajax выполнен, некоторый собственный код будет знать, что это сделано, и поместит событие в очередь JS.

Если в это время не запущен Javascript, событие будет немедленно инициировано, что запустит обработчик ответа ajax. Если в это время что-то работает, событие будет обработано, когда текущий поток выполнения javascript завершится. Никакого опроса движком javascript не требуется. Когда часть Javascript завершает выполнение, JS-движок просто проверяет очередь событий, чтобы увидеть, есть ли что-то еще, что нужно запустить. Если это так, он выталкивает следующее событие из очереди и выполняет его (вызывая одну или несколько функций обратного вызова, которые зарегистрированы для этого события). Если в очереди событий ничего нет, то у интерпретатора JS есть свободное время (сборка мусора или бездействие), пока какой-либо внешний агент не поместит что-то еще в очередь событий и снова не разбудит его.

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

Вот несколько статей о деталях:

jfriend00
источник
Спасибо за это. Я подозревал, что это так, но хорошо знать наверняка. У меня есть цикл for, в котором я отправляю множество запросов ajax. В моем обработчике (для каждого запроса, возвращаемого в произвольном порядке) я запускаю код, который может занять некоторое время. Приятно знать, что это определенно должно сработать.
iPadDeveloper2011,
4
@telandor - события запускаются в порядке FIFO (возможно, есть некоторые исключения крайнего случая, но цель - FIFO). Некоторые события трактуются несколько иначе. Например, события mousemove не накапливаются в очереди (вероятно, потому, что они могут легко переполнить очередь). Когда мышь перемещается и событие mousemove уже находится в очереди, а в очереди нет других более новых событий, оно обновляется с учетом последней позиции, а не добавляется новое событие. Я предполагаю, что события интервального таймера, вероятно, также обрабатываются специально, чтобы избежать их скопления в очереди.
jfriend00 04
2
@telandor - что вам нужно объяснить дальше? Это FIFO. К своему ответу я добавил еще несколько справочных статей. Единственные случаи выполнения FIFO, о которых я знаю, - это немедленно запускаемые события. Вы вызываете .focus()элемент, и это запускает пару других событий, таких как событие «размытия» на элементе с фокусом. Это событие размытия происходит синхронно и не проходит через очередь событий, поэтому оно должно произойти сразу же, прежде чем другие вещи, которые могут быть в очереди событий. На практике я никогда не сталкивался с практическими проблемами.
jfriend00 05
2
@telandor - на документ браузера не должно быть нескольких очередей. Есть одна очередь и все идет последовательно FIFO in / out. Таким образом, тайм-ауты, ответы ajax, события мыши и события клавиатуры помещаются в одну очередь. Тот, кто первым попал в очередь, запускается первым.
jfriend00 05
1
@CleanCrispCode - Спасибо. Я добавил это как полезную ссылку на свой ответ.
jfriend00
16

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

Точнее, посмотрите на заголовки: «Поток событий», «Очередь событий» и «Непользовательские события»: вы узнаете, что:

  1. Javascript выполняется в одном потоке для каждой вкладки или окна браузера.
  2. События ставятся в очередь и выполняются последовательно.
  3. XMLHttpRequest запускается реализацией, а обратные вызовы запускаются с использованием очереди событий.

Примечание. Первоначальная ссылка была: ссылка , но теперь она мертва.

qwertzguy
источник
2

Я хочу немного рассказать о реализации ajax, упомянутой в ответах.

Хотя (обычное) выполнение Javascript не является многопоточным, как это хорошо отмечено в приведенных выше ответах, однако реальная обработка AJAX responses(а также обработка запросов) - это не Javascript, и он, как правило, является многопоточным. (см. реализацию XMLHttpRequest в источнике хрома, которую мы обсудим выше)

и я объясню, возьмем следующий код:

var xhr = new XMLHttpRequest();

var t = Date.now;
xhr.open( "GET", "https://swx.cdn.skype.com/shared/v/1.2.15/SkypeBootstrap.min.js?v="+t(), true );

xhr.onload = function( e ) {
		console.log(t() + ': step 3');
    
    alert(this.response.substr(0,20));
};
console.log(t() + ': step 1');
xhr.send();
console.log(t() + ': step 2');

after an AJAX request is made(- после шага 1), затем, пока ваш js-код продолжает выполнение (шаг 2 и после), браузер начинает настоящую работу: 1. форматирование tcp-запроса 2. открытие сокета 3. отправка заголовков 4. подтверждение связи 5. отправка тело 6. ожидание ответа 7. чтение заголовков 8. чтение тела и т. д. вся эта реализация обычно запускается в другом потоке параллельно с выполнением вашего js-кода. для примера, упомянутая реализация хрома использует Threadable Loader go digg-into 😉 (вы также можете получить некоторое впечатление, посмотрев на сетевую вкладку загрузки страницы, вы увидите несколько одновременных запросов).

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

Шмулик Фридман
источник