Avoiding Callback hell with Async.JS library and Promises
My first slide deck on advanced javascript - avoiding callback hell using asyncjs library and promises libraries. Presented at ManilaJS Feb 2016 meetup
t e d c a l l b a c k s = p y r a m i d o f d o o m v a r f s = r e q u i r e ( ' f s ' ) ; v a r p a r s e S t r i n g = r e q u i r e ( ' x m l 2 j s ' ) . p a r s e S t r i n g ; f s . r e a d F i l e ( " f i l e n a m e " , ' u t f 8 ' , f u n c t i o n ( e r r , f i l e ) { i f ( e r r ) { t h r o w e r r ; } p a r s e S t r i n g ( f i l e , f u n c t i o n ( e r r , r e s u l t ) { i f ( e r r ) { t h r o w e r r ; } i f ( ! ! r e s u l t ) { f s . w r i t e F i l e ( ' / t m p / ' + r e s u l t . t i t l e + ' . j s o n ' , r e s u l t , f u n c t i o n ( e r r , r e s u l t ) { i f ( ! e r r ) { c o n s o l e . l o g ( ' F i l e w r i t t e n t o ' + ' / t m p / ' + r e s u l t . t i t l e + ' . j s o n ' } ) ; } e l s e {
read Hard to Test v a r d r e a m W i t h i n A D r e a m C a l l b a c k = f u n c t i o n ( e r r , d r e a m ) { i f ( e r r ) { c o n s o l e . l o g ( ' S n a p o u t o f d r e a m w i t h i n a d r e a m ' + d r e a m ) ; } e l s e { / / k e e p d r e a m i n g ! r e t u r n c a l l b a c k ( n u l l , ' d r e a m F o r e v e r ' ) ; } } ; v a r f i r s t D r e a m C a l l b a c k = f u n c t i o n ( e r r , d r e a m R e s u l t ) { i f ( e r r ) { c o n s o l e . l o g ( ' S n a p o u t ' ) ; / / s n a p b a c k t o r e a l w o r l d i f s o m e t h i n g s n o t r i g h t } e l s e { d r e a m W i t h i n A D r e a m C a l l b a c k ( n u l l , d r e a m R e s u l t ) ; } } ; f u n c t i o n f i r s t D r e a m ( d r e a m ) { d r e a m S e c o n d D r e a m ( e r r , f i r s t D r e a m C a l l b a c k ) ; } / / s o m e w h a t m i n d - b o g g l i n g t o g e t o u t o f : )
i o n C a l l b a c k = f u n c t i o n ( e r r , d r e a m s ) { / / . . . d o e r r , o r d r e a m s i f s u c c e s s } ; a s y n c . s e r i e s ( [ f i r s t D r e a m , d r e a m S e c o n d D r e a m , d r e a m W i t h i n A D r e a m ] , i n c e p t i o n C a l l b a c k ) ;
series, each one running after previous function has completed. If any tasks pass an error to its callback, the *final* callback is immediately called with the value of the error. Otherwise, *final* callback receives an array of results when tasks have completed. f u n c t i o n m o e s B e e r R e c i p e ( ) { a s y n c . s e r i e s ( [ f u n c t i o n p r e p a r e B r e w ( c a l l b a c k ) { v a r b r e w e d B e e r = b r e w B e e r ( ) ; / / r e t u r n s ' B r e w e d B e e r ' c a l l b a c k ( n u l l , b r e w e d B e e r ) ; } , f u n c t i o n a d d S e c r e t S a u c e ( c a l l b a c k ) { / / A d d s o m e s e c r e t s a u c e v a r s e c r e t S a u c e = ' C o u g h S y r u p ' ; c a l l b a c k ( n u l l , s e c r e t S a u c e ) ; } ] , f u n c t i o n ( e r r , r e s u l t s ) { / / r e s u l t s i s n o w e q u a l t o [ ' B r e w e d B e e r ' , ' C o u g h S y r u p ' ] } ) ; }
(Here the t a s k functions take an ( e r r , r e s u l t ) callback Once the tasks have completed, the results are passed to the final callback as an array. f u n c t i o n b r o w s e r S t a r t u p ( ) { a s y n c . p a r a l l e l ( [ f u n c t i o n c h e c k E m a i l ( c a l l b a c k ) { . . . } , f u n c t i o n g o o g l e S e a r c h ( c a l l b a c k ) { . . . } , f u n c t i o n p l a y Y o u t u b e ( c a l l b a c k ) { . . . } ] , c a l l b a c k ) } ;
in series, and then work with all the result(s) at end? This is a waterfall: Each task starts when the last one completed successfully. If one calls callback(err), we call te finalCallback Similar to series right? (Hint: difference is where you get the result) Note: Agile (and Lean) is probably better a s y n c . w a t e r f a l l ( [ b u i l d A G r e a t B u s i n e s s P l a n ( c a l l b a c k ) , p i t c h T o B i g L e a g u e ( p l a n A , p l a n B , p l a n C , c a l l b a c k ) , w i s h Y o u r s e l f S o m e L u c k ( p l a n A B C , c a l l b a c k ) ] , f u n c t i o n f i n a l C a l l b a c k ( e r r ) { i f ( e r r ) { c o n s o l e . e r r o r ( e ) ; / / p r i n t o u t t h e e r r o r } e l s e { e x e c u t e P l a n ( p l a n A B C ) ; p r o c e s s . e x i t ( 1 ) ; / / e x i t y o u r s t a r t u p } } ) ;
that can either succeed or fail. In Promise-land, we say that the task was either fulfilled* or rejected. a B e a u t i f u l P r o m i s e . t h e n ( f u n c t i o n ( ) { / * T h i s r u n s i f t h e P r o m i s e i s f u l f i l l e d . . . * / } , f u n c t i o n ( ) { / * . . . a n d t h i s r u n s i f t h e P r o m i s e i s r e j e c t e d * / } ) ;
can do this: v a r m y L a m e C a l l b a c k = f u n c t i o n ( e r r , r e s u l t ) { i f ( e r r ) { / / . . t h r o w e r r o r l o g i t o r s o m e t h i n g } r e t u r n r e s u l t ; } ; s o m e F u n c t i o n W i t h A C a l l b a c k ( a r g 1 , m y L a m e C a l l b a c k ) ; m y G r e a t P r o m i s e = m y A w e s o m e F u n c t i o n ( ) ; m y G r e a t P r o m i s e . t h e n ( m y T r e m e n d o u s C a l l b a c k ) ; / / a d d c a l l b a c k s l a t e r m y G r e a t P r o m i s e . t h e n ( m y E v e n B e t t e r C a l l b a c k ) ; / / s t a c k ∞ c a l l b a c k s m y G r e a t P r o m i s e . t h e n ( n u l l , m y E r r o r H a n d l e r ) ; / / r e u s e e r r o r l o g i c
an arbitrary array of Promise-returning task functions? (Was that functional or what?) f u n c t i o n p r o m i s e W a t e r f a l l ( t a s k s ) { f i n a l T a s k P r o m i s e = t a s k s . r e d u c e ( f u n c t i o n ( p r e v T a s k P r o m i s e , t a s k ) { r e t u r n p r e v T a s k P r o m i s e . t h e n ( t a s k ) ; } , r e s o l v e d P r o m i s e ) ; / / i n i t i a l v a l u e r e t u r n f i n a l T a s k P r o m i s e ; } / / r e : . r e d u c e T h e v a l u e r e t u r n e d b y r e d u c e w o u l d b e t h a t o f t h e l a s t c a l l b a c k i n v o c a t i o n
pending. A Promise's state can only change in two ways: pending → fulfilled pending → rejected In short, a Promise can only be fulfilled or rejected once.
return a Promise that’s fulfilled with the results when all tasks have succeeded or rejected with an error when one has failed: Learn : A solid, fast Promises/A+ and when() implementation, plus other async goodies. jQuery calls it w h e n ( ) , calls it a l l ( ) . f u n c t i o n s e n d R e q u e s t s ( s o m e U R L S ) { v a r r e s u l t s = [ ] ; r e q u e s t P r o m i s e s = s o m e U R L S . m a p ( f u n c t i o n ( u r l ) { r e t u r n r e q u e s t ( { u r l : u r l } ) ; } ) ; r e t u r n w h e n . a l l ( r e q u e s t P r o m i s e s ) ; / / R e t u r n a p r o m i s e t h a t w i l l r e s o l v e o n l y o n c e a l l t } when.all when
r P r o m i s e = r e q u i r e ( ' b l u e b i r d ' ) ; v a r f s = P r o m i s e . p r o m i s i f y A l l ( r e q u i r e ( ' f s ' ) ) ; / / t h e n f u r t h e r o n . . f s . r e a d F i l e A s y n c ( ' f i l e s / s o m e . j s o n ' ) . t h e n ( f u n c t i o n ( f i l e D a t a ) { r e t u r n f s . m k d i r A s y n c ( ' f i l e s / m , . , s o m e D i r e c t o r y ' ) ; } ) . t h e n ( f u n c t i o n ( ) { r e t u r n f s . w r i t e F i l e A s y n c ( ' s o m e D i r e c t o r y / s o m e . j s o n ' ) ; } )
in a .then() callback now becomes a rejection value. Resolution state in a .then() is handled by its callback. Exceptions become rejection values and non-thenable returns become fulfillment values. *In jQuery, Promises are said to be resolved on success. But the Promises/A+ spec uses the word “resolved” to mean “either fulfilled or rejected,” and Promises/A+ is awesome. “ jQuery.Deferred objects are updated to be compatible with Promises/A+ and ES2015 Promises. Hence, there were ”
t = j Q u e r y . D e f e r r e d ( ) ; v a r c h i l d = p a r e n t . t h e n ( n u l l , f u n c t i o n ( ) { r e t u r n " b a r " ; } ) ; v a r c a l l b a c k = f u n c t i o n ( s t a t e ) { r e t u r n f u n c t i o n ( v a l u e ) { c o n s o l e . l o g ( s t a t e , v a l u e ) ; t h r o w n e w E r r o r ( " b a z " ) ; } ; } ; v a r g r a n d c h i l d r e n = [ c h i l d . t h e n ( c a l l b a c k ( " f u l f i l l e d " ) , c a l l b a c k ( " r e j e c t e d " ) ) , c h i l d . t h e n ( c a l l b a c k ( " f u l f i l l e d " ) , c a l l b a c k ( " r e j e c t e d " ) ) ] ; p a r e n t . r e j e c t ( " f o o " ) ; c o n s o l e . l o g ( " p a r e n t r e s o l v e d " ) ;
promises are not about callback aggregation. That’s a simple utility. Promises are about something much deeper, namely providing a direct correspondence between synchronous functions and asynchronous functions.” You’re Missing the Point of Promises