I have a chess program that searches moves using a recursive alphaBeta algorithm (and some layers on top of that). I want to stop searching after 10 seconds (but I try with 1 second while making it work), so I use setTimeout to set a global flag.
let timeOut = false;
let i = 0;
setTimeout(() => {
timeOut = true;
console.log('timeout');
console.log('timeout');
console.log('timeout');
console.log('timeout');
}, 1000);
while (i < 1000 && timeOut === false) {
score = mtdf(pos, i, score)
console.log('depth:' + i + ', size: ' + tp.size());
i++;
}
This sure runs in the browser so I know the console.log statements should be printed in the browser but they aren't. The callback isn't called. Help please.
Edit: Added timeOut variable initialisation to make it more clear. Also, sorry, forgot to add i initialization (not that these declarations are even relevant to the question, since the problem is that setTimeout callback isn't even being executed...)
setTimeout will run the function you pass to it:
After the specified time has passed
After the minimum time for a timeout has passed
When the event loop isn't busy doing something else
… whichever is the latest.
You can't use a timeout to interrupt your while loop. The JavaScript engine is too busy running the while loop to check to see if there are any functions waiting to run on the timeout queue.
Your function will be called after the loop has finished when the JS engine is no longer busy.
Don't try to interrupt your loop from the outside.
Before you enter the while loop, record the current time.
Each time you go around the loop, compare the current time with the recorded time.
If the difference is greater than the period you want to allow the loop to run from, exit the loop
Javascript is single threaded meaning that execution of "asynchronous" statements like setTimeout, setInterval, and XMLHttpRequest get queued on a single thread. If something blocks the thread (like your heavy while() loop) async statements get delayed.
See here for more elaborate answer
To make your code work, I suggest looking at the timestamp
var t0 = new Date().getTime(), t1 = t0;
while(true)
{
// do stuff here
t1 = new Date().getTime();
if (t1 % 100 === 0) console.log(t1); // [debug ]print something every 100ms
if (t1 - t0 > 1000) break; // kill the loop after one second
}
setTimeout is asynchronous meaning the program won't wait for the setTimeout to finish and the code will continue executing.
You need to use Promise to wait for the setTimout to finish so your code will behave properly.
Also, the problem of your code also lies in the variable declarations, you didn't declare the i and timeOut variables.
let timeOut = false;
new Promise((resolve) => {
setTimeout(() => {
timeOut = true;
console.log('timeout');
console.log('timeout');
console.log('timeout');
console.log('timeout');
resolve();
}, 1000);
}).then(() => {
let i = 0;
while (i < 1000 && timeOut === false) {
score = mtdf(pos, i, score)
console.log('depth:' + i + ', size: ' + tp.size());
i++;
}
});
Related
I know that this might be a stupid question but it drives me crazy. I'm trying to change the innerHTML of a DOM element but it doesn't change until the end of the function's execution. For example:
function test(){
let testEl = document.getElementById('testEl')
for (let i = 0; i < 5; i++)
{
testEl.innerHTML = 'Count: ' + i;
alert(i);
}
}
Even if I have put an alert in the loop, the text of the element will not change until the end of the function's execution. How can the change be applied instantly (for example I mean during the loop)?
You can update the number every period of time using setInterval:
function test(){
let testEl = document.getElementById('testEl');
let i = 0;
const interval = setInterval(function(){
testEl.innerHTML = `Count: ${i++}`;
if(i === 5)
clearInterval(interval);
}, 1000);
}
test();
<p id="testEl"></p>
JavaScript runs in a single-threaded environment. This means that only one execution context can ever be running at any single point in time. Asynchronous code executes outside of the JavaScript runtime environment (in this case by the browser's native processing) and only when the JavaScript thread is idle can the results of an asynchronous request be executed (i.e. callbacks).
Below is an example that updates a DOM element approximately every second, creating a clock. However, if you click the button, it will ask the browser to render an alert, which is handled outside of the JavaScript runtime and is a blocking UI element, so the clock will stop. Once you clear the alert, you will see the time jump to be roughly current.
As you'll see, the asynchronous API call to window.setInterval() allows for the function to run repeatedly, every so often, and therefore not continuously. This replaces the need for a loop that runs in its entirety every time its accessed. Because of this, you can see updates to the webpage instead of the last value of your loop.
See the comments for more details:
const clock = document.querySelector("span");
// setInterval is not JavaScript. It's a call to a browser
// API asking the JS runtime to run the supplied function every
// 900 milliseconds, but that's just a request. After 900
// milliseconds, the browser will place the function on the
// JavaScript event queue and only when the JavaScript thread
// is idle will anything on the queue be executed. This is why
// the 900 milliseconds is not a guarantee - - it's just the
// minimum amount of time you'll have to wait for the function
// to run, but it could be longer if what's already running
// on the JavaScript thread takes longer than 900 milliseconds
// to complete.
window.setInterval(function(){
// Update the DOM
clock.textContent = new Date().toLocaleTimeString();
}, 900);
document.querySelector("button").addEventListener("click", function(){
// An alert is also not JavaScript, but another browser API that is executed
// by the browser, not JavaScript. However, it is a blocking (modal) UI element.
// The rest of the browser interface (including the web page) cannot update
// while the alert is present. As soon as the alert is cleared, the UI will update.
window.alert("I'm a UI blocking construct rendered by the browser, not JavaScript");
});
<div>Current time is: <span></span></div>
<button>Click for alert</button>
Another way to achieve it is by using async - Promise like this
async function test() {
let testEl = document.getElementById('testEl')
for (let i = 0; i < 5; ++i) {
testEl.innerHTML = 'Count: ' + i;
await new Promise((resolve, _) => {
setTimeout(function() {
resolve("");
}, 1000 /* your preferred delay here */);
});
}
}
I am trying to make a function similar to debounce, except I want the debounced function to be called immediately after the first call and all calls after the first call within the debounce time limit to be debounced. For example, if the function is hooked up to a button, and you rapidly and repeatedly click it 10 times, the function gets called on the first press and gets called a second time after x milliseconds have passed since the tenth press.
Pure JavaScript processing:
Requiring a repeated call within the "debounce" period to be called in addition to the first call adds a complication that can be addressed using a timer in addition to timestamps to make immediate calls if possible.
However, because calls to a function may be delayed, it is not always possible to return a value from the function to the caller in real time.
The following concept code
calls the process "lock" in the sense of locking out calls in a predefined way.
returns undefined to all calls without implementing call backs to allow a caller to tell if its call was actioned or not, or to retrieve a return value;
When a call is made after a lockout period within which addition call attempts were made, the actual arguments used are to place a delayed call are those supplied by the most recent call attempt.
function CallLock( toCall, lockout) {
let argv;
let lastCall = 0;
let timer = 0;
function recall() {
timer = 0;
lastCall = Date.now();
toCall(...argv);
}
return function( ...args) {
let now = Date.now();
if(timer == 0) {
if( now >= lastCall+lockout) {
lastCall = now;
toCall( ...args);
}
else {
argv = args;
timer = setTimeout(recall, lastCall+lockout - now);
}
}
else {
argv = args; // use most recent arguments
}
}
}
// test CallLock
let start;
function demo( msg) {
console.log( "demo('%s') called. Time is %sms after start", msg, Date.now() - start);
}
let lockDemo = CallLock( demo, 1000); // 1 second lockout
start = Date.now();
lockDemo("call 1");
setTimeout( lockDemo, 200, "call 2");
setTimeout( lockDemo, 400, "call 3");
setTimeout( lockDemo, 1800, "call 4");
Test code uses a 1 second lockout time. Note that timer delays are inexact and Date.now() rounds to the nearest millisecond. The expected results are
call 1 is made synchronously and will show a start time of 0 or 1ms.
call 2 is never actioned - its arguments are not used.
call 3 is actioned, but delayed until shortly after the first call lockout
call 4 is actioned, but also delayed because the lockout period from when call 3 was actioned is still in effect.
Sounds like throttle. check this article for difference between throttling and debouncing. if throttle isn't what you need, then you should implement what you need from scratch (and adding more explanation for the details).
Edit:
So, Yes, It is not throttle; It is debounce with invocation on leading edge in lodash;
_.debounce(yourCallback, 100, {
'leading': true
})
I would suggest don't go for debounce. The older debouncing technique relies on setTimeout which is not perfect. Instead try to make use requestAnimationFrame which has in built support for the next trigger for Dom visual states change.
I was reading an article that stated that to create a nonblocking/asynchronous function, usage of setTimeout is crucial. And I thought that the function that is passed to setTimeout runs in the background. Then I read in another article that setTimeout does block the event loop when function is fired. So, I tried the following function to test.
function getit(cb) {
var i = 0;
setTimeout(function() {
while (i < 200000) { i++; console.log(i); }
cb(i);
} , 1000);
console.log(i);
}
getit(function(message) { console.log(message); });
Apparently, when 1000ms passes and it is time to execute the function, the thread is blocked and my browser freezes. My question is, if asynchronous code is not supposed to block the thread, how is that possible when the function is being executed when time elapses and not in the background? It seems like this is just a delay, but eventually, the function is going to be executed line by line and block the loop anyway. Is there something I'm missing or confused about?
Node.js execution model can be represented by the following code:
function main() {
while(running) {
var timerCb = getNextTimedoutCallback();
if( timerCb ) timerCb(); // execute it
var ioCb = getNextCompleteIOOperationCallback();
if( ioCb ) ioCb(); // execute it
}
}
Everything runs in single thread. IO operations run in worker threads and populate internal queue of completed IO operations. That getNextCompleteIOOperationCallback(); just pulls completed operation from the queue.
In the same way setTimeout() simply pushes function alongside with its end time to the timers queue (ordered by end time). And getNextTimedoutCallback() pulls next expired timeout (if any).
As you see either timer function or IO callback can block the whole thing.
That's known as cooperative multitasking.
You first have to understand that JavaScript, under most contexts is single-threaded. Be it asynchronous or not, once your script start executing, it blocks everything.
Your example above therefore enters the busy while-loop after 1000ms and begin blocking other execution.
To overcome this, the busy block has to be broken down to "yield" (Java terminology) execution cycles for other tasks.
E.g.:
function getit(cb) {
setTimeout(function() {
lessBlockingGetIt(cb);
} , 1000);
}
// Note that this is only less blocking, and much slower
function lessBlockingGetIt(cb) {
// Instead of directly looping, we batch-loop with a setTimeout
var numberOfTimes = 1000; // Reduced the size so it doesn't block like forever
var batchSize = 10;
function executeBatch(start) {
// We use a zero-timeout function is achieve a "yield" behavior so other tasks in the event loop can execute
setTimeout(function(){
var i = start, j;
for(j = 0; i < numberOfTimes && j < batchSize; i++, j++){
console.log(i);
}
if(i < numberOfTimes){
executeBatch(i);
} else {
cb(i);
console.log(i);
}
}, 0);
}
// Start the recursion loop
executeBatch(0);
}
getit(function(message) { console.log(message); });
However, you would notice the execution is much slower, and the browser is apparently less responsive than otherwise true multithreading.
To achieve real multithreading, you would need to use web workers http://www.w3schools.com/html/html5_webworkers.asp
This code crashes chrome:
var ms = 1000;
function wait(ms) {
function _done() {
done = true;
alert('timer: ' + timer + ', done: ' + done);
clearTimeout(timer);
}
var done = false;
var timer;
alert('starting timer');
while (!done) {
timer = setTimeout(_done, ms);
}
alert('wait done');
}
Why? How do I get this right?
There are a lot of fundamental problems here.
First, I'm assuming that somewhere not depicted in your code, you're actually calling the wait function by passing it ms.
It looks like you want the variables done and timer to be accessible in your function _done. To ensure that, you should init your variables before you define the function.
setTimeout is async. Javascript is single threaded unless you go out of your way to start up other threads (web workers). setTimeout will not freeze processing for ms amount of time. It adds an event that will run if nothing else is executing. Because it is not blocking, your while loop will loop again to create another timeout, and another... ad infinium. None of the many timeouts will interrupt the while loop because of its single threaded nature. It's more like the Windows Message Queue. Since _done doesn't interrupt and set done to true, it'll continually spin off more and more timeouts until an inevitable crash.
If you're wanting to delay code execution by a certain amount of time, you setTimeout once and execution will begin after that wait as long as nothing else is executing.
function _done() {
alert('done!');
}
setTimeout(_done, 1000);
Another note, I don't think you have to clearTimeout for a timeout that has already timed out. Its only used to prevent a timeout from happening before it times out. This means that nine times out of ten, you can ignore the return value of setTimeout.
When you are setting the timer variable inside the loop, the loop does not wait for the timeout to execute before the loop continues.
So you are overwriting your timeout every time the loop occurs and the code will never be executed because your while loop does not take longer than a second to run the code.
See here for a similar problem : Wait until a condition is true?
You code will essentially change to something like the following :
var ms = 1000;
var done = false;
var timer;
function _done() {
done = true;
alert('timer: ' + timer + ', done: ' + done);
clearTimeout(timer);
wait(ms);
}
function wait(ms) {
if(!done) {
alert('starting timer');
timer = setTimeout(_done, ms);
} else {
//Is Done...
}
}
Once you call the wait function it will check to see if the done variable is true, if not it will call the _done function to set it to true. The _done function will then re-call the wait function to execute the condition again.
you are missing the following line
timer = setTimeout(_done(), ms);
JSFIDDLE
Knowing that while Node.js is working asynchronously, writing something like this:
function sleep() {
var stop = new Date().getTime();
while(new Date().getTime < stop + 15000) {
;
}
}
sleep();
console.log("done");
...would call the sleep(), block the server for the duration of the while loop (15secs) and just THEN print "done" to the console. As far as I understand, this is because Node.js is giving JavaScript only access to the main thread, and therefore this kidn of thing would halt further execution.
So I understand the solution to this is to use callbacks:
function sleep(callback) {
var stop = new Date().getTime();
while(new Date().getTime() < stop + 15000) {
;
}
callback();
}
sleep(function() {
console.log("done sleeping");
});
console.log("DONE");
So I thought this would print 'DONE' and after 15 secs. 'done sleeping', since the sleep() function gets called and is handed a pointer to a callback function. While this function is working (the while loop), the last line would be executed (print 'done'). After 15 seconds, when the sleep() function finishes, it calls the given callback function, which then prints 'done sleeping'.
Apparently I understood something wrong here, because both of the above ways block. Can anybody clarify please?
Thanks in advance,
Slagjoeyoco
Javascript and node.js are single threaded, which means a simple while blocks; no requests/events can be processed until the while block is done. Callbacks don't magically solve this problem, they just help pass custom code to a function. Instead, iterate using process.nextTick, which will give you esentially the same results but leaves space for requests and events to be processed as well, ie, it doesn't block:
function doSleep(callback) {
var stop = new Date().getTime();
process.nextTick(function() {
if(new Date().getTime() < stop + 15000) {
//Done, run callback
if(typeof callback == "function") {
callback();
}
} else {
//Not done, keep looping
process.nextTick(arguments.callee);
}
});
}
doSleep(function() {
console.log("done sleeping");
console.log("DONE");
});
You are calling sleep right away, and the new sleep function blocks. It keeps iterating until the condition is met. You should use setTimeout() to avoid blocking:
setTimeout(function () {
console.log('done sleeping');
}, 15000);
Callbacks aren't the same thing as asynchronicity, they're just helpful when you want to get a... callback... from an asynchronous operation. In your case, the method still executes synchronously; Node doesn't just magically detect that there's a callback and long-running operation, and make it return ahead of time.
The real solution is to use setTimeout instead of a busy loop on another thread.
As already mentioned, asynchronous execution should be achieved by setTimeout() rather than while, because while will freeze in one "execution frame".
Also it seems you have syntax error in your example.
This one works fine: http://jsfiddle.net/6TP76/