JavaScript - однопоточный язык.
Как в таком однопоточном языке, как Javascript, реализуется та самая асинхронность.
Как в таком однопоточном языке, как Javascript, реализуется та самая асинхронность.
В примере ниже в функцию doSomething передаются callback-функции для обработки результата либо ошибки, если результат не удалось получить.
doSomething(function(result) {// success }, function(err) { // error });
Если нужно выполнить несколько функций последовательно, то в каждую функцию надо передавать callback-функцию, которая будет вызвана в конце функции. Пример:
doSomething1(function() {doSomething2(function() { doSomething3(function() { }); }); });
В конце doSomething1 стоит вызов doSomething2, а в конце doSomething2 соотвественно doSomething3.
В следующем примере, все элементы массива обрабатываются асинхронной функцией. Непонятно как сделать что-то важное когда завершится последняя.
Можно использовать рекурсию: для первого элемента вызвать doSomething, в коллбэке рекурсивно вызвать doSomething для следующего элемента, и удалить его из массива. Так до тех пор пока массив не окажется пустым. такие Примерно так же работают и Promises. Они позволяют записывать асинхронные функции в удобном последовательном виде.for (var i = 0; i < a.length; i++) {doSomething(a[i], function() { ... }); }
Асинхронность в JavaScript-приложениях – обычное дело. Любой обмен данными – асинхронный, что HTTP, что чтение файла, что БД. Все просто, если запрос один – callback, и все дела. Если логика сложнее, то приложение в худшем случае превращается в «Callback Pyramid of Doom» или обрастает разной магией. Promise – это подход, который выпрямляет вложенные запросы, превращает «асинхронную лапшу» в структурированный код и делает ваше приложение лучше.
Пример promises на JavaScript:
promise.then(function() {doSomething1(); }).then(function() { doSomething2(); }).then(function() { doSomething3(); });
Каждый вызов then добавляет функцию в очередь функций запланированных на исполнение.
В JavaScript есть всего лишь один способ выполнить функцию отложено — setTimeout с нулевым интервалом.
Код для реализации promises:
function chain(callback) {var queue = []; function _next() { var cb = queue.shift(); if (cb) { cb(_next); } } setTimeout(_next, 0); var then = function(cb) { queue.push(cb); return { then: then } } return then(callback); }
Сокращенная версия (140byt.es):
//137 bytesfunction chain(a){function c(){var a=b.shift();a&&a(c)}var b=[];setTimeout(c,0);var d=function(a){return b.push(a),{then:d}};return d(a)}
queue - очередь запланированных функций.
_next - рекурсивная функция.
Пример использования:
chain(function(next) {console.log('1'); setTimeout(function() { console.log('2'); next(); }, 1000); }).then(function(next) { console.log('3'); setTimeout(function() { console.log('4'); next(); }, 1000); }).then(function(next) { console.log('5'); next(); });
- В функцию chain передается первая callback-функция. В свою очередь callback-функция тоже имеет аргументом callback-функцию next, которую надо вызвать по окончании работы первой callback-функции.
- В функции chain создается место под очередь запланированных функций.
- В функции chain происходит асинхронный вызов функции _next.
- В функции chain происходит вызов функции then, которая добавляет первую callback-функцию в очередь и возвращает в функцию chain объект с собой.
- Функция chain возвращает объект с функцией then.
- В функцию then передается вторая callback-функция.
- Вторая callback-функция добавляется в очередь и снова возвращается объект с функцией then.
- Аналогично добавляется третья callback-функция.
- Начинает работать функция _next. Не забывайте что JS однопоточный язык, поэтому до выполнения функции _next браузер добрался только после того как выполнил основной код и оказался ни чем не занятым поэтому и приступил к выполнению функции _next.
Вот пример с более подробным логом, который помогает понять как браузер выполняет этот код:
function chain(callback) { console.log("<chain>"); var queue = []; function _next() { console.log("<_next>"); var cb = queue.shift(); if (cb) { cb(_next); } console.log("</_next>") } setTimeout(_next, 0); var then = function(cb) { console.log("<then>"); queue.push(cb); console.log("</then>"); return { then: then } } console.log("</chain>"); return then(callback); } chain(function(next) { console.log('1'); setTimeout(function() { console.log('2'); next(); }, 1000); }).then(function(next) { console.log('3'); setTimeout(function() { console.log('4'); next(); }, 1000); }).then(function(next) { console.log('5'); next(); });
Вывод:
<chain> </chain> <then> </then> <then> </then> <then> </then> <_next> 1 </_next> 2 <_next> 3 </_next> 4 <_next> 5 <_next> </_next> </_next>
--
- Обещания JavaScript / Хабрахабр
- Ещё раз о Deferred/Promise / Хабрахабр
- Futures and promises - Wikipedia, the free encyclopedia
- Разгребаем асинхронные задачи с помощью Promise-объектов jQuery
- Promise - JavaScript | MDN
- Справочник javascript: setTimeout
- Управление порядком обработки, setTimeout(...0) | Учебник JavaScript
- Введение в события
- Многопоточный яваскрипт
- VPF::Почему javascript не многопоточный? - Форум программистов
- Как в языках программирования реализуется асинхронное поведение? — Toster.ru
- Про JavaScript (не для JavaScript программистов). | Блог Java программиста
- «Однопоточный программист» / Хабрахабр
- Web workers
- Функции setTimeout и setInterval :: Отложенные вычисления :: Всё только о JavaScript
- Введение в события Javascript