recursion - Understanding Event Queue and Call stack in javascript -
my curiosity understanding concept of "event queue" , "call stack" started when solving question:
var list = readhugelist(); var nextlistitem = function() { var item = list.pop(); if (item) { // process list item... nextlistitem(); } };
the following recursive code cause stack overflow if array list large. how can fix , still retain recursive pattern?
the solution mentioned this:
var list = readhugelist(); var nextlistitem = function() { var item = list.pop(); if (item) { // process list item... settimeout( nextlistitem, 0); } };
solution:
the stack overflow eliminated because event loop handles recursion, not call stack. when nextlistitem runs, if item not null, timeout function (nextlistitem) pushed event queue , function exits, thereby leaving call stack clear. when event queue runs timed-out event, next item processed , timer set again invoke nextlistitem. accordingly, method processed start finish without direct recursive call, call stack remains clear, regardless of number of iterations.
now question:
q1) difference between "event queue" , "call stack"
q2) did not understand answer. can explain me in detail?
q3) when execute function or call variable or object in javascript. how flow go? what goes in call stack? (let's settimeout.. go callstack or event queue?)
these concepts unclear. googled of results aren't expecting understand.
please help!
answer 1 & 3
there big difference between event queue , call stack. in fact, have nothing in common.
call stack (simple overview):
when execute function uses said go on stack, same call stack you're referring there. simplified, it's temporary memory functional execution. or in other words
function foo() { console.log("-> start [foo]"); console.log("<- end [foo]"); } foo();
when called given little sandbox play on stack. when function ends, temporary memory used wiped , made available other things. so, resources used (unless given somewhere system) last long function lasts.
now, if have nested functions
function foo() { console.log("-> start [foo]"); console.log("<- end [foo]"); } function bar() { console.log("-> start [bar]"); foo() console.log("<- end [bar]"); } bar();
here happens when call function:
bar
gets executed - memory allocated on stack it.bar
prints "start"foo
gets executed - memory allocated on stack it. nb!bar
still running , memory there.foo
prints "start"foo
prints "end"foo
finishes execution , memory cleared stack.bar
prints "end"bar
finishes execution , memory cleared stack.
so, execution order bar
-> foo
resolution in last in, first out order (lifo) foo
finishes -> bar
finishes.
and makes "stack".
the important thing note here resources used function released when finishes executing. , finishes execution when functions inside , inside them finish executing. have deep call stack a
-> b
-> c
-> d
-> e
, if large resources held in a
need b
e
finish before released.
in recursion function calls still makes entries on stack. so, if a
keeps calling end call stack of a
-> a
-> a
-> a
etc.
here brief illustration
// naive recursive count down function function recursivecountdown(count) { //show started console.log("-> start recursivecountdown [" + count + "]"); if (count !== 0) {//exit condition //take 1 off count , recursively call again recursivecountdown(count -1); console.log("<- end recursivecountdown [" + count + "]"); // show stopped. terminate stack after line above finished executing; } else { console.log("<<<- it's final recursivecountdown! [" + count + "]"); // show stopped } } console.log("--shallow call stack--") recursivecountdown(2); console.log("--deep call stack--") recursivecountdown(10);
this simplistic , flawed recursive function serves demonstrate happens in case.
event queue
javascript ran in event queue (or "event loop") which, in simple terms, waits "activity" (events), processes them , waits again.
if there multiple events, process them in order - first in, first out (fifo), hence queue. so, if re-write above functions:
function foo() { console.log("-> start [foo]"); console.log("<- end [foo]"); } function bar() { console.log("-> start [bar]"); console.log("<- end [bar]"); } function baz() { console.log("-> start [baz]"); settimeout(foo, 0); settimeout(bar, 0); console.log("<- end [baz]"); } baz();
here how plays out.
baz
executed. memory allocated on stack.foo
delayed scheduling run "next".bar
delayed scheduling run "next".baz
finishes. stack cleared.- event loop picks next item on queue -
foo
. foo
gets executed. memory allocated on stack.foo
finishes. stack cleared.- event loop picks next item on queue -
bar
. bar
gets executed. memory allocated on stack.bar
finishes. stack cleared.
as can see, stack still in play. function call generate stack entry. event queue separate mechanism.
with way of doing things, less memory overhead, since not have way on other function release allocated resources. on other hand, cannot rely on function being complete.
i hope section answers q3.
answer 2
how deferring queue help?
i hope above explanation make more clear bears making sure explanation makes sense:
there set limit of how deep stack be. if think it, should obvious - there memory spare presumably temporary storage. once maximum call depth reached, javascript throw rangeerror: maximum call stack size exceeded
error.
if @ recursivecountdown
example gave above can made cause error - if call recursivecountdown(100000)
rangeerror
.
by putting every other execution on queue, avoid filling stack , avoid rangeerror
. let's re-write function
// still naive bit improved recursive count down function function betterrecursivecountdown(count) { console.log("-> start recursivecountdown [" + count + "]"); if (count !== 0) { //settimeout takes more 2 parameters - after second 1 passed function when gets executed settimeout(betterrecursivecountdown, 0, count - 1); console.log("<- end recursivecountdown [" + count + "]"); } else { console.log("<<<- it's final recursivecountdown! [" + count + "]"); // show stopped } } betterrecursivecountdown(10);
Comments
Post a Comment