Is my assumption right?
Given the following code:
function test() {
for(let i=0 ; i < 100000; i++) {
// Do a set of costly operations that are NOT ASYNCHRONOUS
}
}
test();
myScriptElement.onload = test;
My assumption is "no", there is no chance for myscriptElement.onload to be executed (test function) before the first test call has terminated
… Since JavaScript has a single "thread" which keep on executing instructions until it reaches some asynchronous code, save current callstack/context/etc, put it into a job queue like area, then execute the next eligible item in that queue or wait for one to come in.
Is that right?
Or, should I make sure the first test function call has returned, like with the help of a boolean flag:
let terminated = false;
myscriptElement.onload = function specificHandler(e) {
if (terminated) {
test();
return;
}
setTimeout(specificHandler.bind(null, e), 1000);
};
Thank you.
Related
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.
I created two JS programs to test out the nature of JavaScript call stack, event queue and Timer request handler async API.
Scenario #1.
function logFirst(){
console.log('First');
}
function logThird() {
for (var i = 0; i <= 999999999; i++) {
if (i == 999999999) {
console.log("Third");
}
}
}
logFirst(); //first sync function
setTimeout(function() {
console.log('Second');
}, 0);
logThird(); //painfully slow sync function called
This program executes First, then moves on to encounter setTimeout, which is an async task. Since this task is async, it is handled by the browser's Timer Handler API, which executes a timer for 0 seconds, and then enqueues the anonymous callback function passed to setTimeout inside the event queue of the browser.
But until all of this happens, the third function, a blocking peice of code logThird() has already been called. The call stack is busy since the iteration takes a substantial amount of time to complete. On the last iteration, "Third" is printed to the console.
When logThird() is done executing the call stack becomes free, and the callback that was in the event queue is now pushed to the call stack, and it executes.
Scenario #2.
I created two functions of the blocking kind, like so:
function logFirst(){
console.log('First');
}
function logThird() {
for (var i = 0; i <= 999999999; i++) {
if (i == 999999999) {
console.log("Third");
}
}
}
function logFourth() {
for (var i = 0; i <= 999999999; i++) {
if (i == 999999999) {
console.log("Fourth");
}
}
}
logFirst(); //first sync function
setTimeout(function() { // an async task
console.log('Second');
}, 0);
logThird(); //painfully slow sync function called
//isn't the call stack free at this point in time?
logFourth(); //another painfully slow sync function called
Then I ran this code under the assumption that the call stack would be empty after logThird has finished executing and before logFourth has been pushed to the call stack. So "Second" should have been printed third. But it was printed last.
(Please run both snippets in fullscreen mode to be able to see the JS console output)
Why didn't the async callback get pushed into the call stack?
The call stack would be empty after logThird has finished executing and before logFourth has been pushed to the call stack
No, there still is the global execution context (stack frame) that runs your script and actually pushes logFourth on the stack by calling it. If there wasn't such a thing (which, admittedly, is not a function execution context), every statement of the script would have to go through the event loop itself.
My function OAuth.getSessionInfo returns response still why does the loop goes infinite?
var resp = false;
OAuth.getSessionInfo(function(data) {
resp = true;
})
do {
console.log("waiting............")
} while (!resp);
PS: Please suggest good title for the question.. I am confused on what should be the title for question
Lets remind what multi-threading and concurrency means:
Multi-threading - doing multiple things simultaneously.
Concurrency - switching fast between multiple things, thus emulating them being done simultaneously.
Javascript neither supports the first technique nor the second one. Instead it executes block of code till the end, and then executes next block that was scheduled with setTimeout or setInterval or one that came from event handler (e.g. onclick, onload).
Now if you look at your code you can see that it can't be completed without inner function being completed, but that inner function won't be executed until the outer one completes. And that's why your application hangs. You can also try next code which demonstrates your issue:
setTimeout(function() {
x = false;
console.log("won't be invoked");
}, 0);
var x = true;
while(x) {
console.log('looping');
}
P.S. This javascript's specifics is also the reason why there is no sleep function available - it simply doesn't make any sense to stop the only code executor you have.
You are getting an infinite loop because your while loop keeps running because it doesn't have a point to stop at. The way you have it now it says do this while true. So it keeps running.
Once the loop is started, you haven't updated the value of resp. Try the following:
var resp = false;
do {
OAuth.getSessionInfo(function(data) {
if (data.sessionActive == true) {
resp = true;
} else {
resp = false;
}
});
console.log("waiting............")
} while (!resp);
var wait = function (milliseconds) {
var returnCondition = false;
window.setTimeout(function () { returnCondition = true; }, milliseconds);
while (!returnCondition) {};
};
I know there have been many posts already about why not to try to implement a wait() or sleep() function in Javascript. So this is not about making it usable for implementation purposes, but rather making it work for proof of concept's sake.
Trying
console.log("Starting...");wait(3000);console.log("...Done!");
freezes my browser. Why does wait() seemingly never end?
Edit: Thanks for the answers so far, I wasn't aware of the while loop never allowing for any other code to execute.
So would this work, then?
var wait = function (milliseconds) {
var returnCondition = false;
var setMyTimeOut = true;
while (!returnCondition) {
if (setMyTimeOut) {
window.setTimeout(function() { returnCondition = true; }, milliseconds);
setMyTimeOut = false;
}
};
return;
};
JavaScript is executed in a single thread. Only when an execution path exits can another execution path begin. Thus, when you launch your wait(3000), the following happens:
returnCondition is set to false
a timeout is scheduled
an infinite loop is started.
Each <script> tag, each event being handled, and each timeout (and also UI refresh, in case of a browser) initiate a separate execution path. Thus, a timeout of 3000 is not guaranteed to run in 3000ms, but at any time after 3000ms when the engine is "free".
The wait function never exits, so your script's execution path never ends, and the scheduled timeout's turn never comes.
EDIT:
That means, once a <script> tag has begun, or Node.js has started executing a JavaScript file, the execution has to reach the bottom before anything else can happen. If a function is started as a result of an event or a timeout, that function needs to exit before anything else can happen.
<script>
console.log("script top");
function theTimeout() {
console.log("timeout top");
// something long
console.log("timeout bottom");
}
setTimeout(theTimeout, 0);
setTimeout(theTimeout, 0);
console.log("script bottom");
</script>
There are three execution paths here. The first is the <script> tag's: it starts with printing "script top", schedules two timeouts (for "right now"), then prints "script bottom", and then the end of <script> is reached and the interpreter is idle. That means it has time to execute another execution path, and there are two timeouts is waiting, so it selects one of them and starts executing it. While it is executing, again nothing else can execute (even UI updates); the other timeout, even though it was also scheduled at "immediately", is left to wait till the first timeout's execution path ends. When it does, the second timeout's turn comes, and it gets executed as well.
JavaScript is single threaded. When you call setTimeout the method you passed in as an argument is placed to the async call stack. It means the very next line of code in your block is executing immediately after the setTimeout call and the function you passed in as an argument will execute after your wait method exits.
Your while loop is waiting for a condition which will never happen while the wait function is running because the function which will set your flag will not run until the wait function is done.
The correct way to implement wait is:
var wait = function (milliseconds, onEnd) {
window.setTimeout(function () { onEnd(); }, milliseconds);
};
wait(1000, function(){alert('hi')});
Here you pass in a callback function which will execute after the timeout.
If you have multiple async style calls you can use promises. Promises will make your code easy to read and it will be easy to chain multiple async calls together. There are very good promise librarians: JQuery has $.Deferred built into it but you can use Q if you are writing node.js code.
A promise style implementation would look something like this:
var wait = function (milliseconds) {
var onEnd = null;
window.setTimeout(function () { onEnd(); }, milliseconds);
return {
then: function(action){
onEnd = action;
}
}
};
wait(1000).then(function(){alert('hi')});
https://api.jquery.com/jquery.deferred/
https://github.com/kriskowal/q
The following book helped me a lot to understand this subject:
Async JavaScript: Build More Responsive Apps with Less Code by Trevor Burnham
https://pragprog.com/book/tbajs/async-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.