Комментарии 41
хорошая статья. Читал с удовольствием!
На JS не пишу, но читать было очень интересно — отличная статья! )
Очень полезно для тех, кто работает с JS. Когда я про этот цикл event loop услышал в лекции Крокфорда в 2010, это было доступно только на английском, и вот наконец-то стало массово появляться и у нас. Спасибо за труд!
Спасибо автору!
если setTimeout или setInterval был передано значение таймаута меньше 4 миллисекунд, вместо него использует 4 мс ровно
Уверены?
Первая ссылка — спека w3c:
> If timeout is less than 4, then increase timeout to 4.
Пример:
> If timeout is less than 4, then increase timeout to 4.
Пример:
var i=0,
t=setInterval(function(){i++},1);
setTimeout(function(){
clearInterval(t);
console.log(i) //252
},1000);
В firefox эта цифра зависит, от того фоновая вкладка или нет:
10 ms — если активна
1000ms — если нет
Тут подробности:
10 ms — если активна
1000ms — если нет
Тут подробности:
Скрытый текст
Minimum delay and timeout nesting
Historically browsers implement setTimeout() «clamping»: successive setTimeout() calls with delay smaller than the «minimum delay» limit are forced to use at least the minimum delay. The minimum delay, DOM_MIN_TIMEOUT_VALUE, is 4 ms (stored in a preference in Firefox: dom.min_timeout_value), with a DOM_CLAMP_TIMEOUT_NESTING_LEVEL of 5ms.
In fact, 4ms is specified by the HTML5 spec and is consistent across browsers released in 2010 and onward. Prior to (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2), the minimum timeout value for nested timeouts was 10 ms.
In addition to «clamping», the timeout can also fire later when the page (or the OS/browser itself) is busy with other tasks.
To implement a 0 ms timeout in a modern browser you can use window.postMessage() as described here.
Inactive tabs
In (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) and Chrome 11, timeouts are clamped to firing no more often than once per second (1000ms) in inactive tabs; see bug 633421 for more information about this in Mozilla or crbug.com/66078 for details about this in Chrome.
Google Chrome 26.0.1410.65:
Прыгает 2-4 мс.
console.log(+new Date()); setTimeout(function(){console.log(+new Date())},1)
1367181447810
40745
1367181447812
Прыгает 2-4 мс.
А за что минусы? За то, что Хром у меня показал две миллисекунды вместо каноничных четырёх? :)
методика тестирования взята с потолка — правильно нужно делать тестами и с большим числом N измерений для достоверности.
Для достоверности — это если замерять среднее значение интервала.
А тут выводится таймстэмп, ставится таймер на 1 миллисекунду и снова выводится таймстэмп — получается разница в две миллисекунды вместо ожидаемых четырёх при первой же итерации. Т.е. на четыре миллисекунды полагаться нельзя. Я бы понял, если бы при первой итерации было больше 4 миллисекунд — всякие там задержки на интерпретацию команд, вывод в консоль и т.п. Но получилось меньше.
Впрочем, не представляю, где могут пригодиться такие короткие интервалы с такой точностью, а если даже и нужно ждать именно четыре миллисекунды, а не две, то это можно будет указать в параметрах.
Просто захотелось проверить — благо, консоль всегда под рукой :)
А тут выводится таймстэмп, ставится таймер на 1 миллисекунду и снова выводится таймстэмп — получается разница в две миллисекунды вместо ожидаемых четырёх при первой же итерации. Т.е. на четыре миллисекунды полагаться нельзя. Я бы понял, если бы при первой итерации было больше 4 миллисекунд — всякие там задержки на интерпретацию команд, вывод в консоль и т.п. Но получилось меньше.
Впрочем, не представляю, где могут пригодиться такие короткие интервалы с такой точностью, а если даже и нужно ждать именно четыре миллисекунды, а не две, то это можно будет указать в параметрах.
Просто захотелось проверить — благо, консоль всегда под рукой :)
Короткие интервалы пригодятся, если надо обработать кучу данных, не повесив при этом браузер – разбить обработку на части и вызывать очередную итерацию через эти самые интервалы.
Например, как тут: http://alljs.ru/articles/timeout/fast-settimeout.
Например, как тут: http://alljs.ru/articles/timeout/fast-settimeout.
Имелось ввиду как-то так:
var tt = [];
var date1 = new Date();
var i = 0;
timer = function() {
setTimeout(function() {
i++;
date2 = new Date();
if (i < 100) {
tt.push(date2 - date1);
date1 = date2;
timer();
} else {
console.log(tt);
}
}, 1)
};
timer();
// [2, 1, 2, 1, 4, 5, 5, 4, 4, 5, 4, 4, 4, 5, 4, 4, 4, 5, 4, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 5, 4, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 5, 4, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 4, 5, 4, 4, 5, 4]
Файрфокс я пропустил, виноват, там такого ограничения действительно нет. Может быть, пока что нет, а может быть, и не собираются делать. Ну, и есть, разумеется, браузеры, навроде IE6, которые старше, чем WHATWG, и ни про какие 4 мс не слышали.
У Ильи Кантора есть страничка, где это поведение разбирается гораздо подробнее и в тестах.
В любом случае общее предупреждение «осторожно, оно может длиться намного дольше, чем ожидалось» остается в силе.
У Ильи Кантора есть страничка, где это поведение разбирается гораздо подробнее и в тестах.
В любом случае общее предупреждение «осторожно, оно может длиться намного дольше, чем ожидалось» остается в силе.
Из статьи легко можно понять — что все современные движки JavaScript страдают одним недостатком — они однопоточные. Хотя нет — далеко не все — есть Rhino (по сути JIT транслятор JavaScript в Java)
А вот судьбе node.js не позавидую (сам на нем пишу — поэтому знаю что говорю) — разработчики V8 еще не скоро пофиксят проблемы в x64 и создание/управление Scope
P.S. а сейчас просто жду — может на KickStarter кто-нибудь выдвинет проект по реализации JS движка поверх Erlang — вот тогда будет действительно хорошо — особенно в многопоточности.
А вот судьбе node.js не позавидую (сам на нем пишу — поэтому знаю что говорю) — разработчики V8 еще не скоро пофиксят проблемы в x64 и создание/управление Scope
P.S. а сейчас просто жду — может на KickStarter кто-нибудь выдвинет проект по реализации JS движка поверх Erlang — вот тогда будет действительно хорошо — особенно в многопоточности.
Сколько копий уже сломали — однопоточность в данном случае не недостаток, а достоинство. Надо параллельную обработку — запускай еще одну копию движка. А сделать многопоточность пойдут те же проблемы с синхронизацией, блокировками и т.д. И причем тут Erlang? Там совсем другой подход. И если поверх него городить JS то будет действительно плохо — особенно с производительностью.
Хоть мой коммент заминусовали — однако…
Если Вы писали хоть раз на Erlang распределенные приложения — то поймете как там легко решается проблема с блокировками и доступу к памяти.
На счет cluster — это модуль увы достаточно сырой, а его API нестабилен.
На счет производительности Вы лукавите — сравните node.js в пули из cluster на общих сокетах — против Erlang, это и будет ответом.
Если Вы писали хоть раз на Erlang распределенные приложения — то поймете как там легко решается проблема с блокировками и доступу к памяти.
На счет cluster — это модуль увы достаточно сырой, а его API нестабилен.
На счет производительности Вы лукавите — сравните node.js в пули из cluster на общих сокетах — против Erlang, это и будет ответом.
Ну да легко решается, но если вы реализуете интерпретатор JavaScript(по спецификации) на Erlang вы эту проблему в JavaScript не решите, а в производительности потеряете 100%. Если это будет не по спецификации — то это будет уже не JavaScript.
Про cluster я вообще ни словом не упоминал. Да и он тоже позволяет запускать параллельную обработку. Есть и другие способы.
Если так нравится Erlang и все на нем легко и просто, почему бы просто на нем не писать? Зачем там JavaScript?
Про cluster я вообще ни словом не упоминал. Да и он тоже позволяет запускать параллельную обработку. Есть и другие способы.
Если так нравится Erlang и все на нем легко и просто, почему бы просто на нем не писать? Зачем там JavaScript?
Erlang хорош — но если проект разрабатывается не одним человеком (как правило так и есть), или будет иметь длинный жизненный цикл, или передан позже на утсорс другому человеку — то выбор тут невелик — или PHP или node.js на другое решение вряд-ли согласиться основной заказчик проекта.
Вообще-то для Erlang это решается транзакционными присваиваниями, по аналогии с теми что используются в базах данных.
Учитывая спецификацию — то смотря какую, если Вы решите повернуть сильно в сторону полиморфизма на ECMAScript Harmony — то возможно, а в остальном — Erlang быстрее чем Java, а для Java есть Rhino который очень даже хорош.
Без модуля cluster добиться какой-либо многопоточности проблематично — хотя конечно можно делать spawn процесса и своими силами — но это костыли скорее.
Вообще-то для Erlang это решается транзакционными присваиваниями, по аналогии с теми что используются в базах данных.
Учитывая спецификацию — то смотря какую, если Вы решите повернуть сильно в сторону полиморфизма на ECMAScript Harmony — то возможно, а в остальном — Erlang быстрее чем Java, а для Java есть Rhino который очень даже хорош.
Без модуля cluster добиться какой-либо многопоточности проблематично — хотя конечно можно делать spawn процесса и своими силами — но это костыли скорее.
С++ быстрее чем Java и Erlang и для С++ о чудо! есть NodeJS :-)
Это я к чему: 3 раз намекаю — не факт что реализация JS на Erlang будет быстрее чего-либо.
Это я к чему: 3 раз намекаю — не факт что реализация JS на Erlang будет быстрее чего-либо.
Вы видно не совсем меня понимаете — не нужно путать язык и реализацию. Node.js — это V8 на стероидах.
С++ реализацию естественно она не обгонит — глупо бы было утверждать обратное — но вот в многопоточных задачах думаю Erlang победит (это уже много раз проверялось), и в каких-то моментах обойдет Java/Rhino — поскольку JVM тяжелее чем BEAM или HiPE.
С++ реализацию естественно она не обгонит — глупо бы было утверждать обратное — но вот в многопоточных задачах думаю Erlang победит (это уже много раз проверялось), и в каких-то моментах обойдет Java/Rhino — поскольку JVM тяжелее чем BEAM или HiPE.
Я не понял, где я путал язык и реализацию? Ни NodeJS, ни V8 это не язык.
Erlang может и победит в специфичных многопоточных задачах, а реализация JS на нем не факт. (4й раз, прошу заметить!).
На С++ есть куча реализаций JS. Это не делает их автоматически супербыстрыми, даже на простых задачах. Они все очень разные.
JS по природе не многопоточен. Зачем в него это пихать? Если использовать фишки Erlang в JS — изоляцию процессов, неизменяемость переменных, а только так можно добиться чудесного эффекта — это будет уже не JS. И писать на нем будет так же сложно как на Elang и ваша мечта об аутсорсе не сбудется.
Erlang может и победит в специфичных многопоточных задачах, а реализация JS на нем не факт. (4й раз, прошу заметить!).
На С++ есть куча реализаций JS. Это не делает их автоматически супербыстрыми, даже на простых задачах. Они все очень разные.
JS по природе не многопоточен. Зачем в него это пихать? Если использовать фишки Erlang в JS — изоляцию процессов, неизменяемость переменных, а только так можно добиться чудесного эффекта — это будет уже не JS. И писать на нем будет так же сложно как на Elang и ваша мечта об аутсорсе не сбудется.
Тоже самое можно сказать например и про Lua (тоже однопоточная модель) — однако github.com/rvirding/luerl (очень даже хорошее решение)
Есть memcached подобная реализация хранилища для Erlang — вот и хранилище разделяемых данных.
Естественно что не факт — что реализация на Erlang будет самая быстрая — однако как минимум будет иметь 3 основных преимущества которые в других языках или отсутствуют или осложнены — (1) fault-tolerance (2) виртуальная многопоточность (lightweight-threads) (3) multi-node IPC — что для серверных решений самое то.
Есть memcached подобная реализация хранилища для Erlang — вот и хранилище разделяемых данных.
Естественно что не факт — что реализация на Erlang будет самая быстрая — однако как минимум будет иметь 3 основных преимущества которые в других языках или отсутствуют или осложнены — (1) fault-tolerance (2) виртуальная многопоточность (lightweight-threads) (3) multi-node IPC — что для серверных решений самое то.
Там целый список ограничений. «Хорошее решение» для чего?
Первые два согласен, причем первое вытекает из второго. Но lightweight-threads придется осваивать отдельно — аутсорс не получится.
А multi-node IPC вообще нигде не проблема. На вскидку — ZMQ и вперед.
Первые два согласен, причем первое вытекает из второго. Но lightweight-threads придется осваивать отдельно — аутсорс не получится.
А multi-node IPC вообще нигде не проблема. На вскидку — ZMQ и вперед.
Как встраиваемый язык — Lua для этого и сделан — позволяет легко управлять тяжелой логикой на erlang.
Вообще-то легковестный нити можно положить поверх setTimeout/setInterval/process.nextTick — что даст хорошую совместимость с legacy кодом.
По поводу multi-node IPC Я буду очень рад если Вы сможете показать как сделать легко такую же бесшовную реализацию как и в Erlang (в т.ч. автовыбор порта, сериализация объектов, переподключение, удаленная консоль) — ну и самая вкусная фишка Erlang — это полная связь между IPC и fault-tolerance.
ZMQ — это больше обмен сообщениями, а термин IPC в себя включает НЕ только обмен сообщениями на низком уровне, но и ряд других сервисов.
Вообще-то легковестный нити можно положить поверх setTimeout/setInterval/process.nextTick — что даст хорошую совместимость с legacy кодом.
По поводу multi-node IPC Я буду очень рад если Вы сможете показать как сделать легко такую же бесшовную реализацию как и в Erlang (в т.ч. автовыбор порта, сериализация объектов, переподключение, удаленная консоль) — ну и самая вкусная фишка Erlang — это полная связь между IPC и fault-tolerance.
ZMQ — это больше обмен сообщениями, а термин IPC в себя включает НЕ только обмен сообщениями на низком уровне, но и ряд других сервисов.
Насчет легковесных нитей ничего не понял. Зачем их ложить поверх setTimeout?
Я не собираюсь повторять Erlang, зачем мне что-то показывать?
Термин IPC включает в себя Inter Process Communication. Обмен сообщениями вполне себе коммуникация. Что он там включает в реализации Erlang совершенно не важно для самого термина. Переподключение, например, в некоторых реализациях вообще может не иметь смысла.
Я не собираюсь повторять Erlang, зачем мне что-то показывать?
Термин IPC включает в себя Inter Process Communication. Обмен сообщениями вполне себе коммуникация. Что он там включает в реализации Erlang совершенно не важно для самого термина. Переподключение, например, в некоторых реализациях вообще может не иметь смысла.
Делал свой Event Loop в приложении использующем MS Active Scripting.Поскольку были планы использовать сторонние JS библиотеки то пришлось реализовывать setTimeout setInterval.
Делал давно пошел посмотреть, как у меня там устроено — нашел ошибку. А всегда считал что правильно — работало вроде без проблем.
Делал давно пошел посмотреть, как у меня там устроено — нашел ошибку. А всегда считал что правильно — работало вроде без проблем.
Почитать действительно было интересно. А как тогда правильно следить за изменениями в DOM?
Присоединяюсь к вопросу. (Ветки с ответами более заметны ^_^)
Например, ребята из Mozilla рекомендуют использовать Mutation Observers, если есть возможность.
Очень хорошо текст с примерами реализации сочетается. Спасибо за статью.
Вот только хотелось бы поподробнее про WebWorkers узнать. Как будут себя вести таймеры внутри WebWorker'а и таймеры в основном скрипте и т.д.
Вот только хотелось бы поподробнее про WebWorkers узнать. Как будут себя вести таймеры внутри WebWorker'а и таймеры в основном скрипте и т.д.
Отзывчивость это responsiveness. Responsibility = ответственность.
Отличная статья, интересно.
А этот цикл заставить выполниться никак нельзя? Было бы очень удобно для ожидания асинхронных операций.
А этот цикл заставить выполниться никак нельзя? Было бы очень удобно для ожидания асинхронных операций.
Материал и его подача очень понравились.
Очень бы хотел увидеть от вас в дальнейшем ещё статьи про неочевидные вещи в js.
Очень бы хотел увидеть от вас в дальнейшем ещё статьи про неочевидные вещи в js.
Поправьте опечатку в примере кода для setInterval:
setInterval(function(){
console.log(+new Date());
}, 1000);
setTimeout(function(){
var endDate = +new Date() + 2000; // !!! 2000
while (+new Date() < endDate){
// busy loop for 10 ms <<<-------------- 2000, а не 10
}
}, 1500);
Большое спасибо за статью. Очень понравилась подача и тема очень интересная.
Подскажите, какими источниками пользовались при подготовке? Заранее спасибо.
Подскажите, какими источниками пользовались при подготовке? Заранее спасибо.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Основной цикл в Javascript