Is setTimeout a valid way to avoid stackoverflow when using callbacks? - javascript

Say I want to create some callback hell, such as :
function handleUserInput(callback) {
...
}
function get(uri, callback) {
...
}
function processResponseInWorker(callback) {
....
}
function updateIndexedDB(callback) {
...
}
function updateUI() {
...
}
handleUserInput(get(hot_uri,processResponseInWorker(updateIndexedDB(updateUI()))));
This is mostly academic since the stack only gets 5 high.
In fact, is there even a stack at all? Since these calls will return immediately, and the callback will only be invoked, outside of these contexts, by whatever asynchronous tasks these functions perform.
Okay, just say there IS a call stack here, if each callback was forced to execute inside a setTimeout(func,0) then the calling function would return immediately, the whole chain would return immediately and the functions would be executed off the setTimout queue.
Is that correct?

setTimeout will not call the provided functions unless there is no other code running.
As is demonstrated in this code the timeout, although it is a 0 millisecond delay, will not execute until no other code is being executed. That is the nature of classic javascript (synchronized).
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
console.log(3);
for(var i = 4; i < 100; i++) {
console.log(i);
}
Initially lets assume the depth is 0. Let's assume that no functions other than the callbacks and the functions in the original code are called
updateUI()
Call the function updateUI our depth becomes 1. We return a function from this function and our depth becomes 0 again and we have something that essentially looks like this.
updateIndexedDB(function(){})
So we call updateIndexedDB and our depth becomes 1 which calls the provided callback function and our depth becomes 2. The callback returns and depth becomes 1 and updateIndexedDB returns a functions so that we have something looks like this.
processResponseInWorker(function() {})
Similar process occurs until we have this
get(hot_uri, function() {})
Again the same thing until we have this
handleUserInput(function() {})
Without using timeout the max depth i observe is 2 but using timeouts on your callbacks (which i don't know if you can do because i don't know if your callbacks give you future callbacks) your max is 1 since the callbacks will individually be executed after all code has been executed (on their own stack).
I feel like you meant to write your code this way
handleUserInput(function() {
get(hot_uri , function() {
processResponseInWorker(function() {
updateIndexedDB(function() {
updateUI();
});
});
});
});
which would result in a depth of 6 again unless you use timeouts which would result in a stack size of 1.

Related

Javascript callback function not executing as intended

According to this stackoverflow answer,
functions passed as parameters are always callbacks, even if the intention is that the function is called synchronously...
and I've learned the event loop mechanism which basically says that call back functions are push into a waiting queue and executed after synchronous code finishes(stack empty), however in the following code
function myfun(a, b, callback) {
console.log(a);
callback();
console.log(b);
}
function cb() {console.log('hi')}
myfun(1, 2, cb) // result: 1 hi 2
the result is not 1 2 hi as I expected, from which I infer that only callback functions that fire some 'event signal' like setTimeout will be pushed into such queue but I can't find concrete reference to support it?
"Callbacks" are usually used in conjunction with asynchronous processes like ajax requests or event handlers attached to the ui. We call them "callbacks" in these cases since they need to be executed after something else and it is clear where the program's logic will pick back up this "call" after the async process is complete or triggered brings us "back" here.
Using setTimeout() you can add to the event loop stack. Using a promise you can invoke the stack on the event loop as you wait for an asynchronous process to finish.
Your code doesn't do anything to interrupt the synchronous flow of the code. This snippet shows how even though we have added a timeout of 0 which should delay the action, we can await a promise to allow the event loop's stack to run.
function myfun(a, b, callback) {
console.log(a);
callback();
console.log(b);
}
function cb() {
console.log('hi')
}
myfun(1, 2, cb) // result: 1 hi 2
// same behavior as above
function myStaticFun() {
console.log(1);
cb();
console.log(2);
}
myStaticFun();
// now same as before using a promise to wait a moment and the event loop stack is invoked during the pause
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function myEventLoopInvokingFun(a, b, callback) {
console.log(a);
setTimeout(callback, 0)
await sleep(0);
console.log(b);
}
myEventLoopInvokingFun(1, 2, cb);
It does not necessarily mean that every a callback is asynchronous and must be put into a some task queue and to be executed once synchronous code pieces (call stack is empty) finishes. The callback functions dealing with Promises are candidates for task queue. For instance in your case; cb function simply runs in a synchronous manner; so that the result is 1 hi 2 as you indicated; however if we modify your code as follows:
function myfun(a, b, callback) {
console.log(a);
window.setTimeout(callback, 0);
console.log(b);
}
function cb() {
console.log('hi');
}
myfun(1, 2, cb) // result: 1 2 hi
this will result in 1 2 hi. Even though I set the timeout to just 0 milliseconds; cb function will output after the second console.log within myfun function. I would recommend you to take a look at MDN and this good explanation of call stack, event loop, and task queues. Hope this helps.

Does Self-Invoke JavaScript function will causes stack overflow exception

Here I have a function that calls itself within a setTimeout callback function:
function myFunc(){
// ... I'm doing my jobs here...
setTimeout(function(){
myFunc() //self invoke
},1)
}
myFunc(); // start point
Does this code eventually will occurs stack overflow exception?
Thanks in advance.
No. Functions queued via setTimeout are only run once the main thread (or whatever thread is currently in progress) is complete - there are no nested calls / nested environments that could cause the overflow you're worried about. If you run this snippet, you'll never run into an error, for example:
function myFunc(i) {
if (i % 1000 === 0) console.log(i);
setTimeout(function() {
myFunc(++i)
})
}
myFunc(0);
The same sort of thing is true for functions that invoke promises that call themselves recursively via .then - it's perfectly safe.

Callback function in node.js

I am new to node.js and relatively new to javascript. I have understood how callbacks works and wanted to try out a function myself. Here is my code:
MyScript.js:
var calledfunction = function()
{
console.log("This is a called function");
for(i=0;i<1090660;i++)
{
console.log(i);
}
console.log('done');
};
var sayHello = require('./sayhello.js');
objhello = new sayHello();
objhello.setupSuite(1,calledfunction);
console.log('Next statement;');
sayhello.js
var _ = require('underscore');
module.exports = exports = CLITEST;
function CLITEST(param1,param2)
{
}
_.extend(CLITEST.prototype, {
setupSuite: function (here,callback) {
console.log(here);
console.log('This is a callback function');
callback();
}
})
The above program is run by executing > node Myscript.js
My question is : the for loop consumes 50 secs to execute and print all the numbers in the console and then only executes the line "Next statement" which is outside the callback function .
Why is this happening? because I read theories saying that the immediate statements will be executed without having to wait for the function to get executed.
The ideal output should have been : print " Next statement" and then print the contents of the for loop
but in the above case it is vice versa ?
This is not a real callback, but rather a simple function call. Function calls are obviously synchronous, as the following statements may rely on their return values.
In order to may the callback async you can use: setTimeout or setImmediate, depending on the actual use case.
See also: Are all Node.js callback functions asynchronous?
As pointed out by one of the commenters, your code is executed in a synchronous fashion. The function calls are executed one after the other, thus no magic is happening and the console.log('Next statement;'); call is executed after the execution of the callback. If you were in a situation in which you had to call a function which executed an asynchronous call (i.e., an AJAX call with a callback) then yes, you would expect the subsequent console.log to be executed right after the asynchronous call.
In other words, in your case the code represents a simple function call, while an asynchronous call would offload the computation of the callback somewhere else, thus the execution of the code where the callback function was called keeps going by printing the statement and won't wait for the execution of the callback.

Mechanics behind Javascript callbacks being asynchronous

I'm having a bit of trouble understanding the mechanics of JS callbacks. I have a fair idea of how callbacks can be used in JS, but I do not understand how a callback is asynchronous.
For e.g., if my understanding is correct, a callback is of the nature:
db.query(param1, param2 , callback_fn1(){..} );
And the implementation of db.query() is along the lines of :
db.prototype.query = function(p1 , p2 , callback ){
//some code
callback();
}
How does the above implementation make db.query an asynchronous function? Does this not mean that a function called callback is passed to query and that function is called inside query? It looks like query is just another synchronous function. Could someone help me understand what I'm overlooking here? Thanks!
The code sample you've shown is actually still synchronous because is instructed to run immediately. An asynchronous callback is a callback that doesn't immediately need to be executed, so it doesn't block the event loop until you instruct it to run.
The most common way in Node.js to do this is with process.nextTick() which runs a specified function when the event loop call stack is empty. Here's an example:
var async = function(args, callback) {
// do some work
process.nextTick(function() {
callback(val);
});
};
Then we call the function like this:
async(args, function(val) {
console.log(val);
});
console.log('end');
In this example, the function async() and console.log('end') are added to the call stack. The call stack empties once both of those functions are run, and once it's empty, console.log(val) then runs.
If you're still confused, think of process.nextTick() as an optimized version of this code:
var fn = function() {};
setTimeout(fn, 0);
It basically means "run this function as soon as possible when you are not busy".
Edit: I just now realized the question is tagged with node.js. My answer is more about Javascript in the browser, #hexacyanide's answer is more about node.js. I guess knowing both doesn't hurt, though!
The way you posted it the code will indeed be blocking. For asynchronous behavior there are a few things you can utilize, such as
setTimeout and setInterval
Built-in, asynchronous methods such as from the FileReader API
Ajax requests
Web workers (see #html5rocks)
Your example code could be written as follows (fiddle):
function doStuff(callback) {
setTimeout(function () {
for (var i = 0; i < 1000; i++) {
// do some busy work
var x = Math.sqrt(i);
}
callback();
}, 0);
}
console.log('start');
doStuff(function () {
console.log('callback called');
});
console.log('after doStuff()');
The setTimeout call will allow the Javascript interpreter/compiler (however exactly they work these days) to run the function in a non-blocking matter which is why (most likely), you will see the output
start
after doStuff()
callback called
Note that asynchronousity is different from multi-threading. Javascript is still single-threaded (with the exception of web workers!).
A more in-depth explanation can be found for example here

How to write a node.js function that waits for an event to fire before 'returning'?

I have a node application that is not a web application - it completes a series of asynchronous tasks before returning 1. Immediately before returning, the results of the program are printed to the console.
How do I make sure all the asynchronous work is completed before returning? I was able to achieve something similar to this in a web application by making sure all tasks we completed before calling res.end(), but I haven't any equivalent for a final 'event' to call before letting a script return.
See below for my (broken) function currently, attempting to wait until callStack is empty. I just discovered that this is a kind of nonsensical approach because node waits for processHub to complete before entering any of the asynchronous functions called in processObjWithRef.
function processHub(hubFileContents){
var callStack = [];
var myNewObj = {};
processObjWithRef(samplePayload, myNewObj, callStack);
while(callStack.length>0){
//do nothing
}
return 1
}
Note: I have tried many times previously to achieve this kind of behavior with libraries like async (see my related question at How can I make this call to request in nodejs synchronous?) so please take the answer and comments there into account before suggesting any answers based on 'just use asynch'.
You cannot wait for an asynchronous event before returning--that's the definition of asynchronous! Trying to force Node into this programming style will only cause you pain. A naive example would be to check periodically to see if callstack is empty.
var callstack = [...];
function processHub(contents) {
doSomethingAsync(..., callstack);
}
// check every second to see if callstack is empty
var interval = setInterval(function() {
if (callstack.length == 0) {
clearInterval(interval);
doSomething()
}
}, 1000);
Instead, the usual way to do async stuff in Node is to implement a callback to your function.
function processHub(hubFileContents, callback){
var callStack = [];
var myNewObj = {};
processObjWithRef(samplePayload, myNewObj, callStack, function() {
if (callStack.length == 0) {
callback(some_results);
}
});
}
If you really want to return something, check out promises; they are guaranteed to emit an event either immediately or at some point in the future when they are resolved.
function processHub(hubFileContents){
var callStack = [];
var myNewObj = {};
var promise = new Promise();
// assuming processObjWithRef takes a callback
processObjWithRef(samplePayload, myNewObj, callStack, function() {
if (callStack.length == 0) {
promise.resolve(some_results);
}
});
return promise;
}
processHubPromise = processHub(...);
processHubPromise.then(function(result) {
// do something with 'result' when complete
});
The problem is with your design of the function. You want to return a synchronous result from a list of tasks that are executed asynchronously.
You should implement your function with an extra parameter that will be the callback where you would put the result (in this case, 1) for some consumer to do something with it.
Also you need to have a callback parameter in your inner function, otherwise you won't know when it ends. If this last thing is not possible, then you should do some kind of polling (using setInterval perhaps) to test when the callStack array is populated.
Remember, in Javascript you should never ever do a busy wait. That will lock your program entirely as it runs on a single process.
deasync is desinged to address your problem exactly. Just replace
while(callStack.length>0){
//do nothing
}
with
require('deasync').loopWhile(function(){return callStack.length>0;});
The problem is that node.js is single-threaded, which means that if one function runs, nothing else runs (event-loop) until that function has returned. So you can not block a function to make it return after async stuff is done.
You could, for example, set up a counter variable that counts started async tasks and decrement that counter using a callback function (that gets called after the task has finished) from your async code.
Node.js runs on A SINGLE threaded event loop and leverages asynchronous calls for doing various things, like I/O operations.
if you need to wait for a number of asynchronous operations to finish before executing additional code
you can try using Async -
Node.js Async Tutorial
You'll need to start designing and thinking asynchronously, which can take a little while to get used to at first. This is a simple example of how you would tackle something like "returning" after a function call.
function doStuff(param, cb) {
//do something
var newData = param;
//"return"
cb(newData);
}
doStuff({some:data}, function(myNewData) {
//you're done with doStuff in here
});
There's also a lot of helpful utility functions in the async library available on npm.

Categories

Resources