Геть callback hell або як працюють promises Santa Simplicita

Просто писати про просте — не так просто…

Геть callback hell або як працюють promises?

Піст буде недовгим. Всі хто писав на джаваскрипті (особливо під Node.js) знають, що мова це однопоточна, і будь-які тривалі (блокуючі) операції зазвичай оформлюють у функції такого виду:

Відповідно, якщо потрібно виконати кілька дій поспіль послідовно, виходить некрасива конструкція:

Особливо у багатьох виникає питання — а як бути, якщо є масив елементів, для кожного з них потрібно викликати асинхронну функцію і зробити щось важливе, коли завершиться остання. Очевидно, що звичайний for тут уже не допоможе:

Деякі хоробрості використовують рекурсію: для першого елемента викликати doSomething, в коллбеку рекурсивно викликати doSomething для наступного елемента, і видалити його з масиву. Так поки масив не виявиться порожнім. такі Приблизно так само працюють і Promises. Вони дозволяють записувати асинхронні функції у зручному послідовному вигляді. Код виглядає приблизно так:

Як це працює?

Очевидно, що кожен виклик then() додає функцію-аргумент у чергу запланованих функцій, проте поки що не виконує їх. А коли їх виконувати?

  • черга відкладених функцій
  • об'єкт з методом then() для додавання нових функцій у чергу
  • рекурсивна функція, яка виконує першу відкладену функцію з черги та чекає на її завершення
  • setTimeout(fn, 0) для запуску рекурсивної функції

Як дізнатися, коли саме завершуються функції? Найпростіше робити це явно – коли функція готова завершитись – нехай викличе спеціальну функцію next(). Звучить важко тому що часто зустрічається слово «функція»? Зараз,дивимося на код:

Тут все як описано вище - черга, рекурсивна функція, setTimeout та об'єкт із методом then().

У прикладі на екран виводиться "1", потім через секунду - "2" і "3", ще через секунду - "4" і "5".

Код можна стиснути практично до розміру твіту (137 байт) - https://gist.github.com/zserge/7021626 Можливо навіть додам його в 140byt.es.

А далі можна зробити так, щоб next() приймала аргументи, наприклад, результат роботи функції та об'єкт-помилка. А можна переглянути існуючі аналоги Promises/Futures. Головне, що тепер зрозуміло, що у них там усередині.