Promise – это не больно

Promise – это не больно

Асинхронность в JavaScript-приложениях – обычное дело. Любой обмен данными – асинхронный, что HTTP, что чтение файла, что БД. Все просто, если запрос один – callback, и все дела. Если логика сложнее, то приложение в худшем случае превращается в «Callback Pyramid of Doom» или обрастает разной магией. Promise – это подход, который выпрямляет вложенные запросы, превращает «асинхронную лапшу» в структурированный код и делает ваше приложение лучше. Вы всё еще боитесь использовать Promise? Тогда приходите на мой доклад.

Video https://vimeo.com/74925301

B827d6cfdfbcfce33700b0e6cc03e344?s=128

Mikhail Davydov

August 02, 2015
Tweet

Transcript

  1. Promise — это не больно! Михаил Давыдов, JavaScript-разработчик 2013 год,

    FrontTalks
  2. Асинхронность везде!

  3. Запросы к серверу 03

  4. © 2013 NAVTEQ Ошибка на карте? · Условия использования Данные

    пользователя 04
  5. G A R M O S H K A События

    интерфейса 05 ∞
  6. Таймеры и анимация 06

  7. None
  8. Когда один callback — всё здорово!

  9. l o g i n ( ' u s e

    r : p a s s @ s e r v e r ' , f u n c t i o n ( e r r , s e r v e r ) { i f ( e r r ) r e t u r n c b ( e r r ) ; s e r v e r . o p e n ( ' d b ' , f u n c t i o n ( e r r , d b ) { i f ( e r r ) r e t u r n c b ( e r r ) ; d b . q u e r y ( q u e r y , f u n c t i o n ( e r r , v i e w ) { } ) ; } ) ; } ) ; Последовательные запросы 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . 09
  10. v a r r o w s = [ ]

    , t o t a l = v i e w . l e n g t h ; f u n c t i o n f e t c h ( e r r , r o w ) { i f ( e r r ) r e t u r n c b ( e r r ) ; i f ( r o w s . l e n g t h = = = t o t a l ) c b ( n u l l , r o w s ) ; } f o r ( v a r i = 0 ; i < t o t a l ; i + + ) { v i e w . g e t ( i , f e t c h ) ; } Параллельные запросы 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . 0 8 . 10
  11. Шум l o g i n ( ' u s

    e r : p a s s @ s e r v e r ' , f u n c t i o n ( e r r , s e r v e r ) { i f ( e r r ) r e t u r n c b ( e r r ) ; s e r v e r . o p e n ( ' d b ' , f u n c t i o n ( e r r , d b ) { i f ( e r r ) r e t u r n c b ( e r r ) ; d b . q u e r y ( q u e r y , f u n c t i o n ( e r r , v i e w ) { } ) ; } ) ; } ) ; 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . 11
  12. • Статус ошибки • Однообразная обработка ошибки • Глубина вложенности

    Шум 12
  13. d b . q u e r y ( q

    u e r y , f u n c t i o n ( e r r , v i e w ) { } ) ; Как отменить или игнорировать запрос в этом случае? d b . q u e r y ( q u e r y , f n ) . a b o r t ( ) ; / / . c a n c e l ( ) ? d b . q u e r y ( q u e r y , f u n c t i o n ( e r r ) { i f ( e r r . m e s s a g e = = = ' a b o r t ' ) r e t u r n ; } ) ; Сложно отменить действие 0 1 . 0 1 . 0 2 . 0 3 . 0 4 . 13
  14. q u e r y ( q u e r

    y 1 , f u n c t i o n ( e r r , v i e w ) { } ) ; q u e r y ( q u e r y 2 , f u n c t i o n ( e r r , v i e w ) { } ) ; q u e r y ( q u e r y 3 , f u n c t i o n ( e r r , v i e w ) { } ) ; 3 запроса — 3 попытки логина. Для кэша нужно еще дописать код. Несколько обработчиков 0 1 . 0 2 . 0 3 . 14
  15. f u n c t i o n u b

    e r Q u e r y G e n e r a t o r ( d a t a ) { / / Г о т о в и м q u e r y F r o m D a t a r e t u r n q u e r y ( q u e r y F r o m D a t a ) ; } f u n c t i o n u b e r Q u e r y G e n e r a t o r ( d a t a , f n ) { q u e r y ( q u e r y F r o m D a t a , f n ) ; } Делегирование результата 0 1 . 0 2 . 0 3 . 0 4 . 0 1 . 0 2 . 0 3 . 15
  16. У callback-ов нет единого интерфейса

  17. • Последний аргумент • Свойство конфига или метод – success

    – complete – done – finish – ??? Нет единого интерфейса 17
  18. $ ( ' d i v ' ) . a

    n i m a t e ( { } , f u n c t i o n ( ) { } ) ; / / А м о ж н о е щ е в о т т а к $ ( ' d i v ' ) . a n i m a t e ( { } , { c o m p l e t e : f u n c t i o n ( ) { } } ) ; Нет единого интерфейса 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 18
  19. $ . a j a x ( { u r

    l : u r l s u c c e s s : f u n c t i o n ( ) { } } ) ; $ . a j a x ( { u r l : u r l } ) . d o n e ( f u n c t i o n ( ) { } ) ; Нет единого интерфейса 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . 19
  20. Магия

  21. • Step.js • Streamline.js • Fibers • ??? Магия 21

  22. S t e p ( f u n c t

    i o n l o g i n ( ) { l o g i n ( ' u s e r : p a s s @ s e r v e r ' , t h i s ) ; } , f u n c t i o n o p e n D a t a b a s e ( e r r , d b ) { i f ( e r r ) t h r o w e r r ; d b . q u e r y ( q u e r y , t h i s ) ; } ) ; Step.js 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 22
  23. v a r d b = l o g i

    n ( ' u s e r : p a s s @ s e r v e r ' , _ ) ; d b . q u e r y ( q u e r y , _ ) ; Вроде бы ничего! Streamline.js 0 1 . 0 2 . 23
  24. Streamline.js ( f s t r e a m l

    i n e _ _ . c r e a t e ( f u n c t i o n ( _ ) { v a r d b = ( y i e l d f s t r e a m l i n e _ _ . i n v o k e ( n u l l , l o g i n , [ ' u s e r : p a s s @ s e r v e r ' , _ ] , 1 ) ) ; ( y i e l d f s t r e a m l i n e _ _ . i n v o k e ( d b , " q u e r y " , [ q u e r y , _ ] , 1 ) ) ; ; y i e l d ; } , 0 ) . c a l l ( t h i s , f u n c t i o n ( e r r ) { i f ( e r r ) t h r o w e r r ; } ) ) ; 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . 24
  25. None
  26. • aka Futures • Распостранен в других языках • Стандартизирован

    Promise/A+ • Стандарт «Покрыт тестами» Promises/A+ Compliance Test Suite • Готовые абстракции над fs, http, ... Promise 26
  27. l o g i n ( ' u s e

    r : p a s s @ s e r v e r ' ) . t h e n ( f u n c t i o n ( s e r v e r ) { r e t u r n s e r v e r . o p e n ( ' d b ' ) ; } ) . t h e n ( f u n c t i o n ( d b ) { r e t u r n d b . q u e r y ( q u e r y ) ; } ) Promise 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . 27
  28. . t h e n ( f u n c

    t i o n ( v i e w ) { r e t u r n f e t c h R o w s ( v i e w ) ; } , f u n c t i o n h a n d l e E r r o r ( e r r ) { c o n s o l e . e r r o r ( e r r ) ; } ) / / . . . Меньше шума, обработка ошибок в одном месте Promise 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 28
  29. • Обещание можно сдержать, а можно не выполнять • Состояние

    изменяется только 1 раз • Результат сохраняется • Цепочка обещаний • Можно пообещать нескольким Абстракция над обещанными данными 29
  30. v a r P r o m i s e

    = f u n c t i o n ( ) { t h i s . _ v a l u e = n u l l ; t h i s . i s F u l f i l l e d = f a l s e ; / / o k t h i s . i s R e j e c t e d = f a l s e ; / / f a i l t h i s . i s R e s o l v e d = f a l s e ; / / o k | | f a i l / / . . . } ; Реализация Promise 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . 30
  31. P r o m i s e . p r

    o t o t y p e = { / / @ r e t u r n { P r o m i s e } t h e n : f u n c t i o n ( o n F u l f i l l e d , o n R e j e c t e d ) { } , f u l f i l l : f u n c t i o n ( d a t a ) { } , r e j e c t : f u n c t i o n ( e r r o r ) { } } ; Реализация Promise 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 31
  32. / / @ p a r a m { N

    u m b e r } t i m e / / @ r e t u r n { P r o m i s e } f u n c t i o n t i m e o u t ( t i m e ) { v a r p r o m i s e = n e w P r o m i s e ( ) ; s e t T i m e o u t ( p r o m i s e . f u l f i l l , t i m e ) ; r e t u r n p r o m i s e ; } Использование Promise 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . 32
  33. t i m e o u t ( 1 0

    0 0 ) . t h e n ( f u n c t i o n ( ) { c o n s o l e . l o g ( ' f i r s t ' ) ; r e t u r n t i m e o u t ( 1 0 0 0 ) ; } ) . t h e n ( c o n s o l e . l o g . b i n d ( c o n s o l e , ' s e c o n d ! ' ) ) ; Пример timeout 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . Run 33
  34. t i m e o u t ( 1 0

    0 0 ) . t h e n ( r a n d o m L o g ) . t h e n ( c o n s o l e . l o g . b i n d ( c o n s o l e , ' d o n e ! ' ) ) ; f u n c t i o n r a n d o m L o g ( ) { v a r r n d = M a t h . r a n d o m ( ) ; c o n s o l e . l o g ( r n d ) ; i f ( r n d < 0 . 5 ) r e t u r n t i m e o u t ( 2 0 0 0 ) ; } Пример timeout 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . 0 8 . Run 34
  35. • Не хак и не магия • Единый интерфейс всех

    промисов • Меньше шума в коде • Разделение логики на шаги • ... • PROFIT! Promise 35
  36. Трюки с Promise

  37. / / $ . w h e n - а

    г р е г а т о р P r o m i s e / / Р е з у л ь т а т н е р а н ь ш е , ч е м ч е р е з 1 с $ . w h e n ( $ . g e t ( ' / ' ) , $ . g e t ( ' / ? ' ) , t i m e o u t ( 1 0 0 0 ) ) . t h e n ( f u n c t i o n ( r e s ) { c o n s o l e . l o g ( r e s [ 0 ] . l e n g t h + r e s [ 1 ] . l e n g t h ) ; } ) ; Склеивание Promise 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . Run 37
  38. n e w A t t e m p t

    ( g e t 4 0 4 , r e p e a t 3 T i m e s ) . t h e n ( o k , e p i c F a i l , p r o g r e s s ) ; f u n c t i o n g e t 4 0 4 ( ) { r e t u r n $ . g e t ( ' / 4 0 4 ' ) ; } f u n c t i o n r e p e a t 3 T i m e s ( e r r , n u m ) { i f ( n u m < 4 ) r e t u r n 1 0 0 0 ; } Повтор Promise 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . 0 8 . Run 38
  39. v a r c a c h e ; f

    u n c t i o n r e q u e s t ( ) { r e t u r n c a c h e ? c a c h e : c a c h e = $ . g e t ( ' / ' ) ; } r e q u e s t ( ) . t h e n ( f u n c t i o n ( h t m l ) { c o n s o l e . l o g ( h t m l . l e n g t h ) ; } ) ; Прозрачный кэш с Promise 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 0 7 . Run 39
  40. • Браузер – $.Deferred – $.when, $.ajax, $.get, $.post, ...

    • Node.js – Q: q-io – Vow: vow-fs, vow-asker • DOM Promises! WHATWG Promise Spec Promise уже рядом! 40
  41. None
  42. • Решение проблем асинхронности будущего • Оператор y i e

    l d «Ставит выполнение кода на паузу» • Когда? – Node.js 0.11.x c - - h a r m o n y - g e n e r a t o r s – Браузеры на V8 3.19 c - - h a r m o n y - g e n e r a t o r s – Firefox 2+ (старый синтаксис) • Можно транслировать в ES5, но лучше это не делать... Generators 42
  43. v a r c o = r e q u

    i r e ( ' c o ' ) ; c o ( f u n c t i o n * ( ) { v a r s e r v e r = y i e l d l o g i n ( ' u s e r : p a s s @ s e r v e r ' ) , d b = y i e l d s e r v e r . o p e n ( ' d b ' ) , v i e w = y i e l d d b . q u e r y ( q u e r y ) ; } ) ; Скоро на экранах ваших IDE Generators 0 1 . 0 2 . 0 3 . 0 4 . 0 5 . 0 6 . 43
  44. yield vs then?

  45. • Именно «и», а не «VS» • Используем вместе –

    Библиотека Q – Библиотека co • С Generators код еще чище! • Обработка ошибок с try/catch! Generators и Promise 45
  46. • Готов к использованию сегодня! • Единый интерфейс • Стандарт

    Promise/A+ • Структурирует код • Меньше шума в коде • Куча библиотек: Vow, Q, jQuery Используйте Promise! 46
  47. None
  48. None
  49. Promise — это не больно! Михаил Давыдов JavaScript-разработчик @azproduction 49

  50. • Примеры с презентации • Promises/A+ • Differences from Promises/A

    • Design of Q library (настоятельно рекомендую) • Iterators & Generators • A Study on Solving Callbacks with JavaScript Generators • A Closer Look at Generators Without Promises Почитать 50
  51. c l c k . r u / 8 i

    9 p r