Avoiding stack overflow by using setTimeout

后端 未结 3 834
耶瑟儿~
耶瑟儿~ 2021-02-09 20:42

I\'ve found the following question here:

The following recursive code will cause a stack overflow if the array list is too large. How can you fix this a

3条回答
  •  执念已碎
    2021-02-09 21:17

    This is just a hacky alternative to trampolines, which in turn are just a hacky alternative to TCO.

    When you call a function in Javascript, you add a frame to the call stack. That frame contains information about the variables in the scope of the function and how it was called.

    Before we call a function, the call stack is empty.

    -------
    

    If we call function foo, then we add a new frame to the top of the stack.

    | foo |
    -------
    

    When foo finishes executing, we pop the frame off the stack again, leaving it empty again.

    Now, if foo in turn calls another function bar, then we'll need to add a new frame onto the stack, whilst foo is executing.

    | bar |
    | foo |
    -------
    

    Hopefully you can see that if a function calls itself recursively it keeps adding new frames to the top of the call stack.

    | ...          |
    | nextListItem |
    | nextListItem |
    | nextListItem |
    | nextListItem |
    ----------------
    

    Recursive functions will keep adding frames until either they finish processing, or they exceed the max length of the call stack, resulting in an overflow.

    Because setTimeout is an asynchronous operation, it doesn't block your function, which means nextListItem will be allowed to finish and its frame can be popped off the call stack—preventing it from growing. The recursive call will be handled with the event loop instead.

    Is this pattern ever useful? The max size for the call stack depends on your browser, but it can be as low as 1130. If you wanted to process an array with a few thousand elements using a recursive function, then you'd risk blowing the call stack.

    Trampolines use a similar technique, but rather than offloading the work to the event loop, you return a function which calls the next iteration instead, then the calls can be managed with a while loop (which doesn't affect the stack).

    var nextListItem = function() {
      var item = list.pop();
    
      if (item) {
        // process the list item...
        return nextListItem;
      }
    };
    
    while(recur = recur()) {}
    

提交回复
热议问题