Используется ли setTimeout или setInterval для запуска потока?

Я читал эту статью http://ejohn.org/blog/how-javascript-timers-work/ и как setTimeout и setInterval и другие задачи async, такие как кнопка click confused me маленький.

Я знаю, что JS - это однопоточная, то есть AFAIK, все функции обратного вызова (обработчики событий a.k.a.) будут поставлены в очередь и выполняться в порядке. Однако посмотрите на изображение ниже, которое я взял из статьи, связанной выше:

enter image description here

Каждый блок представляет некоторую работу, и - около 10 мс - таймер запускается. Я знаю, что его функция обратного вызова помещается в очередь для последующего выполнения, но как получилось, что событие может быть вызвано, пока что-то уже выполняется?

Это потому, что setTimeout() начинает использовать отдельный поток, чтобы подсчитать время внутри и запустить его событие завершения?

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

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

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

Ответ 1

JavaScript однопоточный, что означает, что два фрагмента JavaScript не будут выполняться одновременно (если не использовать worker, но каждый рабочий также является одиночным -threaded). Но JavaScript - это не единственная игра, когда дело доходит до API среды. Функции типа setTimeout, setInterval и addEventListener реализованы изначально (или, по крайней мере, за пределами однопоточного JavaScript), и их обратные вызовы запускаются средой, например браузером. Это среда помещает эти обратные вызовы в очередь, которые должны выполняться одним поточным механизмом JavaScript. Именно так браузер может принимать новые события кликов, которые будут запущены, хотя он все еще выполняет какой-либо другой код JavaScript. Именно по этой причине JavaScript считается управляемым событиями.

Ответ 2

В настоящее время (Ecmascript 6) вам никогда не придется беспокоиться о том, что состояние меняется на полпути внутри функции.

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

Ответ 3

Нет необходимости иметь другие потоки, чтобы объяснить это поведение. Когда запись таймера добавляется в очередь, она просто сидит там. Если цикл события снова получает управление, он просто проверяет, есть ли запланированная задача, время которой вверх или нет.

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

Возьмем, к примеру, этот простой PhantomJS script:

function longRunningTask() {
    for(var i = 0; i < 100000; i++) {
        var s = "",
            s2 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        for(var j = 0; j < 1000; j++) {
            s += s2;
        }
    }
}

var start = new Date().getTime();
console.log("begin");
setTimeout(function(){
    console.log("end timer 1s " + (new Date().getTime() - start));
}, 1000);

setTimeout(function(){
    console.log("end timer 10s " + (new Date().getTime() - start));
}, 10000);
longRunningTask();
console.log("end longRunningTask " + (new Date().getTime() - start));

setTimeout(function(){
    console.log("EXIT");
    phantom.exit();
}, 11000);

Что производит следующий вывод:

begin
end longRunningTask 5025
end timer 1s 5029
end timer 10s 10001
EXIT

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