jquery deferred setTimeout loop - javascript

I have an array of functions to iterate with setTimeout function to give non-blocking effects, but any or all function can have order flag, which means this is to be executed only after previous functions have been executed. Someone suggested me to use jquery.deferred. I've never used jquery deferred.
while(that.funcns.length>0){
fn=that.funcns.splice(0,1)[0];
fn.idx=i;
window.setTimeout(function(){
fn.ref(); //call function reference
},(idx==0)?idx:1000);
}
//fn - {ref:functionReference,order:true/false};

You could use deferred objects, but why don't you just use one timer and call the functions one by one?
var funcs = [/* array of functions */];
function next() {
var func = funcs.shift();
if(func) {
func();
setTimeout(next, 100);
}
}
next();
Things get more complicated if some functions can run "in parallel" and some are dependent, but you don't provide a lot of information about this.
But it wouldn't make much of a difference either. If you don't use webworkers, any JavaScript is run sequentially, even if you use setTimeout. Just the order of execution is not determined.

If I understand your question, each function you put in the list can have a flag that says, "Wait to execute me, until all previous functions have been executed". Therefore what you need to do is add a function count and code to each function you execute to decrement the count. Something like this, I put a copy in jsFiddle here:
var funcCount = 0, funcList = [];
function executeFunctions() {
var nextFunc;
while (funcList.length > 0) {
// Check next in list, if we need to wait, make sure we wait
nextFunc = funcList[0];
if (nextFunc.needToWait) {
if (funcCount > 0) {
// Try again later
setTimeout(executeFunctions, 100);
return;
}
}
// Since we are now going to execute, remove from list and execute
funcList.splice(0, 1);
funcCount += 1; // nextFunc will subtract 1 on completion
setTimeout(nextFunc, 100);
}
}
// For async functions to call back to completion
function completionCallback() {
funcCount -= 1;
}
To test it I have defined two functions. The first simulates async with a long timeout. The second has the wait flag set so it needs to wait for the first. Then I add them both to the list and test it:
// Example function 1 is simulated async
function example1() {
alert("Example1");
// Simulate async call with completion callback function, e.g. XHttpRequest
setTimeout(completionCallback, 2000);
}
example1.needToWait = false; // Not flagged
// Example function is flagged as need others to complete first
function example2() {
alert("Example2");
funcCount -= 1;
}
example2.needToWait = true;
// Setup function list to execute example1 then example2
funcList.push(example1);
funcList.push(example2);
// OK, test it
executeFunctions();
If you change the function2 flag to false, the alert boxes show up one after the other, right away. If you leave it as true, the second one doesn't show up until the 2 seconds have elapsed.

Related

How to execute a function in Javascript with a delay and a flag condition (event handler-like)?

I'm writing a program in Javascript that takes input strings and then runs a simulation on each string. The user decides how fast, i.e. what the delay should be between processing each string. I'm using the setInterval() function to control this. However, I am running into the issue that longer strings may not be ready to process because the last string is still processing. This causes a slew of errors on my part. Here's some code to paint a better picture.
let testingInterval = setInterval(function () {
strprn.innerHTML = `<h2>${strings[i]}<\h2>`; // displays current string to user
if (i + 1 == strings.length) { // checks if should notify user all strings have been processed
checker.finalCheck = true;//the checker uses this flag to notify the user once the test completes
}
checker.check(strings[i]); //runs the check i.e. simulation
i++; // increments the counter iterating through the array (setup code not shown here)
if (i >= strings.length) {
clearInterval(testingInterval); //once we reach the end stop the interval iterating
evenOutResults(); // clean up answers function
updateTimeStamp(Date.now()); // for readability, I add a timestamp of when the results were generated
}
}, delay); // user specified delay
What I'm looking for is a way to honor the delay but also not begin the next call until the current string has finished processing.
Something like this logically (the code below freezes your browser XD):
function delayLoop() {
setTimeout(function () {
strprn.innerHTML = `<h2>${strings[i]}<\h2>`;
if (i + 1 == strings.length){
checker.finalCheck = true;
}
checker.check(strings[i]);
i++;
if (i < strings.length) {
// check if the current string has finished, if so call, else wait until the string is done
while (checker.processingFlag){
// console.log('Waiting for current string to finish');
}
delayLoop(); // call again
} else {
evenOutResults();
updateTimeStamp(Date.now());
}
}, delay);
Correct me if I'm wrong, but it seems like as though you want to have some kind of schedule of appointments that you'd like "messages" to be received at and if a message is not ready at the appointment, then you'd like it to reschedule to the next appointment. You can easily find the next available appointment with some kind of iteration
const nextCheckin = (lastCheckin, interval) => {
while (lastCheckin < Date.now())
last += delay
return last
}
Assuming the order of messages matters you can do something like such
const simulation = (strings, delay) => {
let checkin = Date.now() + delay
for (const str of strings) {
const result = simulate(str)
checkin = nextCheckin(checkin, delay)
console.log(`Waiting for: ${checkin-Date.now()}`)
while (Date.now() < checkin)
continue
reportWork(result)
}
}
The while loop will cause the event loop to hang, so maybe a call to setTimeout would be more appropriate, but whatever floats a boat.
Sorry, I should have clarified this more. Checker.check() is a function that uses a setInterval() to display an animation. The animation needs to be done for a set of objects. Because of the setInterval(), anytime we wait means javascript will try to execute the next lines of code. This means vanilla for-loops are out of the question.
My initial solution was to throw the for-loop iteration itself into a setTimeout(). This works as long as the delay is long enough for all the objects being iterated. Problem is the objects rarely are, so if an object was larger than the one it preceded and the delay was short then the whole animation crashed. In the end, Promises were the easiest solution.
let runLoop = async () => {
for(var i = 0; i < strings.length; i++){
strprn.innerHTML = `<h2>${strings[i]}<\h2>`;
console.log("about to await");
if (i + 1 == strings.length){
checker.finalCheck = true;
}
await new Promise(resolve => checker.check(resolve, strings[i]));
if (checker.finalCheck){
updateTimeStamp(Date.now());
}
}
}
runLoop();
For those who wonder onto this looking for an answer, the await pauses your execution until the resolve is met. You pass resolve onto your function, and inside the setInterval() code block, at the very end you call resolve(). In order to use the await, the whole thing gets wrapped up inside of an async.

Can a function determine whether the last call of that function occured synchronously in the event loop?

Say I have a function fn which is called elsewhere under unpredictable circumstances, and I want to be able to distinguish between consecutive synchronous calls of the function, and asynchronous calls of the function. That is, distinguish between:
// synchronous
fn();
doSomething();
fn();
and
// asynchronous
fn();
setTimeout(fn);
// also asynchronous
Promise.resolve()
.then(fn);
fn();
If the next call of fn is during a separate task (or message), it's relatively easy: the callback passed Promise.resolve().then will run as a microtask, just before the current message (macrotask) finishes, so one can check/set a flag when the function is called, and reset it in the microtask.
const fn = (() => {
let calledDuringThisMessage = false;
return (arg) => {
console.log('fn running', arg);
if (calledDuringThisMessage) {
console.log('Duplicate synchronous call!');
}
calledDuringThisMessage = true;
Promise.resolve()
.then(() => {
calledDuringThisMessage = false;
});
};
})();
fn(1);
fn(2);
setTimeout(fn, 0, 3);
This works when the possible-asynchronous calls of fn are separated by macrotasks. But if they're separated by a microtask like the .then of a Promise, is there any way to differentiate between them?
If possible, I would imagine that this would involve listening for an event (or something) that occurs once the stack is empty, or the ability to get a unique identifier from the top function in the current stack, or something like that.
Of course, if I have control over where fn is being called, I could change the code there to watch for duplicate calls by setting a flag somewhere (in the function at the top of the stack, if need be), but is this possible internally?
im really hoping, this is helping you. You can "register" functions so that they are put in a custom stack when they are called.
const MY_FUNCTION_STACK = [];
function register(func) {
return function (...args) {
MY_FUNCTION_STACK.push(func);
return func(...args);
}
}
function test1() {
return 1;
}
test1 = register(test1);
test1(); // returns 1
test1(); // returns 1
MY_FUNCTION_STACK; // returns [test1(), test1()]
this will not alter the original functions so you can use it to register third party methods.
You could also add timestamps or whatever additional informations about the functions you want (and sort it later by DateTime).
Also with this solution you can choose if you want to add a function call to the stack
at the begigining of a Promise
or the end of a Promise
or only if the promise gets resolved
...
you have the full control.
EDIT:
The below example is usuable if you only want certain functions to log. If you want to log all functions you can use the following code:
const originalPrototypeCall = Function.prototype.call;
Function.prototype.call = function call(...args) {
MY_FUNCTION_STACK.push(this);
return originalPrototypeCall.bind(this)(...args);
}
Please remeber that altering system prototypes is no good practise and can cause a lot of unexpactable trouble.

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

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.

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.

Join 2 'threads' in javascript

If I have an ajax call off fetching (with a callback) and then some other code running in the meantime. How can I have a third function that will be called when both of the first 2 are done. I'm sure it is easy with polling (setTimeout and then check some variables) but I'd rather a callback.
Is it possible?
You could just give the same callback to both your AJAX call and your other code running in the meantime, use a variable to track their combined progress, then link them to a callback like below:
// Each time you start a call, increment this by one
var counter = 0;
var callback = function() {
counter--;
if (counter == 0) {
// Execute code you wanted to do once both threads are finished.
}
}
Daniel's solution is the proper one. I took it and added some extra code so you don't have to think too much ;)
function createNotifier() {
var counter = 2;
return function() {
if (--counter == 0) {
// do stuff
}
};
}
var notify = createNotifier();
var later = function() {
var done = false;
// do stuff and set done to true if you're done
if (done) {
notify();
}
};
function doAjaxCall(notify) {
var ajaxCallback = function() {
// Respond to the AJAX callback here
// Notify that the Ajax callback is done
notify();
};
// Here you perform the AJAX call action
}
setInterval(later, 200);
doAjaxCall(notify);
The best approach to this is to take advantage of the fact that functions are first-order objects in JavaScript. Therefore you can assign them to variables and invoke them through the variable, changing the function that the variable refers to as needed.
For example:
function firstCallback() {
// the first thing has happened
// so when the next thing happens, we want to do stuff
callback = secondCallback;
}
function secondCallback() {
// do stuff now both things have happened
}
var callback = firstCallback;
If both your pieces of code now use the variable to call the function:
callback();
then whichever one executes first will call the firstCallback, which changes the variable to point to the secondCallback, and so that will be called by whichever executes second.
However your phrasing of the question implies that this may all be unnecessary, as it sounds like you are making an Ajax request and then continuing processing. As JavaScript interpreters are single-threaded, the Ajax callback will never be executed until the main body of code that made the request has finished executing anyway, even if that is long after the response has been received.
In case that isn't your situation, I've created a working example on my site; view the source to see the code (just before the </body> tag). It makes a request which is delayed by the server for a couple of seconds, then a request which receives an immediate response. The second request's response is handled by one function, and the first request's response is later handled by a different function, as the request that received a response first has changed the callback variable to refer to the second function.
You are talking about a thing called deferred in javascript as #Chris Conway mentioned above. Similarly jQuery also has Deferred since v1.5.
Check these Deferred.when() or deferred.done()
Don't forget to check jQuery doc.
But to give you some idea here is what I am copying from that site.
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
/* a1 and a2 are arguments resolved for the
page1 and page2 ajax requests, respectively */
var jqXHR = a1[2]; /* arguments are [ "success", statusText, jqXHR ] */
if ( /Whip It/.test(jqXHR.responseText) ) {
alert("First page has 'Whip It' somewhere.");
}
});
//Using deferred.then()
$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
.then(myFunc, myFailure);
Something like this (schematic):
registerThread() {
counter++;
}
unregisterThread() {
if (--counter == 0) fireEvent('some_user_event');
}
eventHandler_for_some_user_event() {
do_stuff();
}
You can do this easily with Google's Closure library, specifically goog.async.Deferred:
// Deferred is a container for an incomplete computation.
var ajaxFinished = goog.async.Deferred();
// ajaxCall is the asynchronous function we're calling.
ajaxCall( //args...,
function() { // callback
// Process the results...
ajaxFinished.callback(); // Signal completion
}
);
// Do other stuff...
// Wait for the callback completion before proceeding
goog.async.when(ajaxFinished, function() {
// Do the rest of the stuff...
});
You can join multiple asynchronous computations using awaitDeferred, chainDeferred, or goog.async.DeferredList.

Categories

Resources