64th: Recursive use of asynchronous loops

Source: Internet
Author: User


Item 64:use recursion for asynchronous Loops

Consider a function that takes an array of URLs and tries to download one at a time until one succeeds. If the API were synchronous, it would is easy-to-implement with a loop:

 function   Downloadonesync (URLs) { Span style= "color: #0000ff;"  >for  (var  i = 0, n = urls.length;  I < n; I++ try    return  Downloadsync (Urls[i]);} catch   (E) {}}  throw  new  Error ("All downloads failed" );}  

But this approach won ' t work for downloadoneasync, because we  Can ' T suspend a loop and resume it in a callback. if we  tried using a loop, it would initiate all of the  Downloads rather than waiting for one to continue before trying  the next: 

 function   Downloadoneasync (URLs,  Onsuccess, onerror) { for  (var  i =  0, n = urls.length;  I < n; I++   //   ?  });  //  loop continues  }  throw  new  Error ("All Downloads Failed ");}  

So we need to implement something that acts like a loop, &NBSP;BUT&NBSP;THAT&NBSP;DOESN ' T continue executing until we explicitly say  so. The solution is to implement the loop as a  function, so we can decide when to start each iteration: 

 function   Downloadoneasync (URLs, Onsuccess, onfailure) { var  n = Urls.lengt H  function   Trynexturl (i) { if  (i >= N) {onfailure ( "All downloads Failed"); return   function   () {Trynexturl (i  + 1 0);}  

the local trynexturl function is recursive: its implementation  involves 
a call to itself. now, in typical javascript  environments, a recursive 
function that calls itself synchronously  can fail after too many calls 
To itself. for example,  this simple recursive function tries to call itself 
100,000   times, but in most javascript environments it fails with  a 
runtime error: 

 function   Countdown (n) { if  (n = = = 0 return  "done" ;  else   { return  Countdown (N-1);}}  Countdown ( 100000); //  Error:maximum call stack size exceeded  

So how could the recursive Downloadoneasync was safe if Countdown explodes when n is too large? To answer this, let's take a small detour and unpack the error message provided by Countdown.

JavaScript environments usually reserve a fixed amount of space in
Memory, known as the call stack, to keep track of how to do next
Returning from function calls. Imagine executing this little program:

 function   negative (x) { return  ABS (x) * -1;}  function   ABS (x) {  Math.Abs (x); } console.log (negative ( 42)); 

at the point in the application where math.abs is called  With the argument 42, there are several other function calls  in progress, each waiting for another to return. figure 7.2  illustrates the call stack at this point. at the point  of each function call, the bullet symbol  (•)  depicts the   place  in  the  program  where  a   Function  call  has  occurred  and where that call  will return to when it finishes. like the traditional stack  data structure, this information follows a  "Last-in, first-out"   protocol: the most recent function call that pushes information onto the stack  ( Represented as the bottommost frame of the stack)  will be  The first to pop back off the stack. when math.abs finishes,  it returns to the abs function, which returns to the  Negative function, which in turn returns to the outermost script.  

When a program is in the middle of too many function  calls, it can run 
Out of stack space, resulting in  a thrown exception. this condition is 
known as stack  Overflow. in our example, calling countdown (100000)  
requires   Countdown  to  call  itself  100,000  times,  each   time  pushing 
another stack frame, as shown in  figure  7.3. the amount of space 
Required to store so  many stack frames exhausts the space allocated 
by most  javascript environments, leading to a runtime error. 

Now take another look at Downloadoneasync. Unlike Countdown, which
Can ' t return until the recursive call returns, Downloadoneasync only
Calls itself from within an asynchronous callback. Remember that
Asynchronous APIs return Immediately-before their callbacks are
Invoked. So Downloadoneasync returns, causing it stack frame to be
Popped off of the call stack, before any recursive call causes a new
Stack frame to being pushed back on the stack. (In fact, the callback is
Always invoked in a separate turn of the event loops, and each turn of
The event loop invokes its event handler with the call stack initially

Empty.) So Downloadoneasync never starts eating up call stack space, no matter how many iterations it requires.

Things to Remember

? Loops cannot be asynchronous.

? Use recursive functions to perform iterations in separate turns of the event loop.

? Recursion performed in separate turns of the event loops does not overflow the call stack.

Article from: Effective+javascript 68 effective ways to write high-quality JavaScript code English version

64th: Recursive use of asynchronous loops

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

Tags Index: