Rxjs increment loop counter after limit - javascript

I have sample code of observable to print some values
sample4Observable() {
var observable = Observable.create(function (observer) {
for (var i = 1; i <= 4; i++) {
if (i <= 3) {
observer.next(i);
}
if (i === 4) {
setTimeout(() => {
observer.next(i);
observer.complete();
}, 5000);
}
}
});
console.log('just before subscribe');
observable.subscribe({
next: x => console.log('got value ' + x),
error: err => console.error('something wrong occurred: ' + err),
complete: () => console.log('done'),
});
console.log('just after subscribe');
}
the output is
just before subscribe
got value 1
got value 2
got value 3
just after subscribe
got value 5
done
My question is my loop is end when the value reached to 4. So the next value will be 4 instead of 5. why RxJS skip the value 4?
In order to get the value for 4 i can change my code like below. But in above scenario I am little bit confusing. Can anyone explain
setTimeout((i) => {
observer.next(i);
observer.complete();
}, 5000,i);

It isn't relate anything with RxJS anyway, this is the javascript execution happens. Since you're looping up until i<=4, count is reaching to 4 and once i==4 you're firing up a function with setTimeout. So as soon setTimeout gets registered, it doesn't get evaluate directly. setTimeout is async event, browser place it inside something called as Event Loop. And wait till all synchronous execution of javascript get over. Since you had i++ in for loop i value becomes 5. Afterwards all synchronous execution completion. Javascript visit Event Loop to evaluate asynchronous operation(they can be events as well). That time your setTimeout evaluate and it wait till 5000(5 sec), then it gets i value as 5 as expected.
The point is even if you set timeout 0 instead of 5000 you will get same result i.e. i = 5.
Since javascript is single threaded, it always evaluate synchronous code followed by asynchronous code.
There you could pass i value as parameter to setTimeout function while calling it. Provided i value will be available inside the scope of function directly, so that wouldn't checking the global value. Check MSDN Docs here
setTimeout((i) => {
observer.next(i);
observer.complete();
}, 5000,i);

When you set a timeout, the inner function will be executed after the time has passed. When the loop reaches i === 4 it starts the timeout, the inner function is not yet executed. The loop goes one more round incrementing i to 5, then the loop terminates because the condition i <= 4 is not met anymore. After the 5 seconds have passed (which is after i has been incremented to 5), the timeout is fulfilled and the function is executed. Although when the timeout was started i === 4 was true, now when the timeout function is executed, it is i === 5.
This is not directly linked to RxJS. This simpler example should illustrate it. Just run it in your browser console:
let i = 0;
setTimeout(() => console.log(`the value of i is ${i}`), 500);
i++;

Related

for loop with several consecutive setTimeout calls not repeating correctly?

Long story short, i'm working in the chrome developer console and i have a javascript function (that i decided to call main for illustration) that makes 4 function calls but each of the 4 function calls need to be separated by a few seconds to allow the page to update properly and the user to react to the resulting dialogue boxes.
The main function WORKS beautifully when i add a setTimeout with a delay to each of the 4 function calls inside and run it ONCE. it does NOT work when i try to loop the main function X times.
From what i can gather from reading other similar problems and documentation, this has to do with the complexities of how setTimeout works and how the javascript engine in the chrome browser is single-threaded... or something? Not my expertise but i tried to do my due diligence, ultimately i do not know how to fix the problem and even searching other Q/A's and documentation i could not arrive at a working solution
I've tried the below code with the setTimeout( , delay * i) method and with the setTimeout( , delay) with no index from the for loop being passed in to multiply the delay, both did not work correctly.
Other questions recommend using nested setTimeouts, i'm not sure how to accomplish that here? and others recommend using setInterval, but i'm not sure how to accomplish that a programmatic amount of times, as each of my surveys has a certain number of questions, and i don't want the number of repetitions of the main function to exceed the maximum amount of questions in a survey.
for(let i = 1; i < 15+1; i++) {
main(i);
}
function main(i){
setTimeout(takescreenshot, 2000*i);
setTimeout(checkforanswers,5000*i);
setTimeout(takescreenshot, 8000*i);
setTimeout(function(){$("[name=next_question_button]")[0].click();},11000*i);
}
The main function is supposed to loop an arbitrary number of times each time, and in my head, the program flow is supposed to be:
execute the "takescreenshot" function after a 2 second delay from executing main.
execute the "checkforanswers" function 5 seconds after main is called and 3 seconds after the first function is called in main. this gap between step 1-2 allows the user to respond to the print dialogue box that arises.
3.execute the "takescreenshot" function 8 seconds after main is called, and 3 seconds after the previous step, allowing the page DOM elements to be updated correctly for the new screenshot to reflect changes from the first screen shot.
execute a click on the "next_question" button to advance to the next question in my survey, 11 seconds after main is called and 3 seconds after the last screen shot is taken to advance onto the next question, allowing 4 steps to be repeated on a new question of the survey.
When executed once, it works exactly as I want, but when looped multiple times, step 1-4 begin to occur without the proper delay and start to happen before the relevant "wait for X" in each of the steps has been satisfied and it all mushes together achieving unpredictable and unwanted results.
If i is 2 then setTimeout(takescreenshot, 2000*i); will be called 2000*2 milliseconds after main was called.
But you want that it is called 11000 + 2000 milliseconds after main was called. The same is for the other timeouts.
As i starts with 1 you want to use 11000 * (i - 1) as a delay added to each timeout function.
function main(i){
let delay = 11000 * (i - 1);
setTimeout(takescreenshot, 2000 + delay );
setTimeout(checkforanswers, 5000 + delay );
setTimeout(takescreenshot, 8000 + delay );
setTimeout(function(){$("[name=next_question_button]")[0].click();}, 11000 + delay);
}
Your algorithm is off. You should be multiplying i by the time it takes for a full iteration to complete, and then adding 2, 5, or 8 seconds to it instead. For example, if the desired time from one iteration's takescreenshot to the next iteration's takescreenshot is 14000 ms, use:
function main(i){
setTimeout(takescreenshot, 2000 + (i * 14000));
setTimeout(checkforanswers,5000 + (i * 14000));
setTimeout(takescreenshot, 8000 + (i * 14000));
setTimeout(function(){$("[name=next_question_button]")[0].click();},11000 + (i * 14000));
}
The order of operations would be clearer if you promisified the function and awaited each call in the for loop:
(async () => {
for (let i = 1; i < 15 + 1; i++) {
await main();
}
})();
function main() {
setTimeout(takescreenshot, 2000);
setTimeout(checkforanswers, 5000);
setTimeout(takescreenshot, 8000);
return new Promise((resolve) => {
setTimeout(function() {
$("[name=next_question_button]")[0].click();
resolve();
}, 11000);
});
}
Here is the classic chunking approach:
function chunks([first, ...other], delay = 2000, result) {
const nextResult = first(result);
setTimeout(() => chunks(other, delay, nextResult), delay);
}
const simpleFunction = () => { return 'whatever'; };
const functionWithParams = (a, b) => a + b;
const pipe = [
simpleFunction,
() => functionWithParams(3, 5),
data => console.log('inline code to run, which uses prev result (3+5=8): ' + data),
];
chunks(pipe);
// or chunks(pipe, 5000);
Your for loop does not wait so you are just making timeout calls that are milliseconds apart for the entire loop. So you are basically calling takescreenshot 15 times.
Just use main to call itself. In the last timeout, call main to repeat.
function main(i){
setTimeout(takescreenshot, 2000*i);
setTimeout(checkforanswers,5000*i);
setTimeout(takescreenshot, 8000*i);
setTimeout(function(){
$("[name=next_question_button]")[0].click();}
if (i<14) {
main(i+1)
}
}, 11000*i)
}
main(0)

setTimeout not calling callback to interrupt while loop

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++;
}
});

Mocha not respecting timeout or done callback when running CasperJS tests

So here is the issue that I'm trying to solve with casperjs and mocha. I'm trying to test an element's text value on a page to see if its updating over a period of... 5-10 seconds. The idea being I grab the value, push it in to an array, wait 500ms and repeat until the array has 20 items. Thats about 10 seconds. Then run underscore/lodash's _.uniq function on the array and test that the array length is > 1.
The problem I'm running in to is that mocha is not waiting for this to complete because stating the test is a success/failure. I thought I could increase mocha's timeout but this has not made any difference. Please see the code below. I've commented it for readability.
it('has elements whose values update', function () {
// 20 seconds, which should be plenty of time
this.timeout(20000);
casper.then(function() {
// The test array
var values = [],
// So we can stop the intervals
intervalId;
function getValue () {
// Grab element's text value
var value = casper.evaluate(function () { return $('#element').text(); });
// Push in to our test array
values.push(value);
// 20 * 500ms == 10 seconds or 10000ms
// When set to 500ms, this block never runs. The test passes before it has a chance to
if (values.length === 20) {
// Stop it from checking the value any further
clearInterval(intervalId);
// Test to see we've had more than one unique value in the 10 seconds
expect(_.uniq(values).length).to.be.gt(1);
}
}
// Wait for the value on the page to populate
// It defaults to '-' when the page loads
casper.waitFor(function () {
return this.evaluate(function () {
return $('#element').text() !== '-';
});
// Start the check with a delay of 500ms between each check
}, function then() {
intervalId = setInterval(getValue, 500);
});
});
});
With the interval value set at 500ms I get 2-3 element values in values before mocha moves on to the next test. Even odder is when I console.log(values) they are printing on screen AFTER mocha as determined the test passed. The reason is that values.length never gets to 10 so the expect call is never called. The test is assumed to be passing. Here is the test output at 500ms interval:
Dashboard
✓ has elements whose values update (202ms)
Values: ["20,832,022"]
Values: ["20,832,022","20,832,372"]
Values: ["20,832,022","20,832,372","20,832,722"]
✓ has the page title of leads (41ms)
2 passing (11s)
It passes even though there isn't 20 items. It never checks it due to a timeout somewhere. Here is the output with 50ms interval:
Dashboard
✓ has elements whose values update (341ms)
Values: ["20,400,667"]
Values: ["20,400,667","20,400,718"]
Values: ["20,400,667","20,400,718","20,400,718"]
Values: ["20,400,667","20,400,718","20,400,718","20,400,769"]
Values: ["20,400,667","20,400,718","20,400,718","20,400,769","20,400,769"]
Values: ["20,400,667","20,400,718","20,400,718","20,400,769","20,400,769","20,400,820"]
Values: ["20,400,667","20,400,718","20,400,718","20,400,769","20,400,769","20,400,820","20,400,820"]
Values: ["20,400,667","20,400,718","20,400,718","20,400,769","20,400,769","20,400,820","20,400,820","20,400,871"]
Values: ["20,400,667","20,400,718","20,400,718","20,400,769","20,400,769","20,400,820","20,400,820","20,400,871","20,400,871"]
Values: ["20,400,667","20,400,718","20,400,718","20,400,769","20,400,769","20,400,820","20,400,820","20,400,871","20,400,871","20,400,922"]
Final Values: ["20,400,667","20,400,718","20,400,718","20,400,769","20,400,769","20,400,820","20,400,820","20,400,871","20,400,871","20,400,922"]
✓ has the page title of leads (41ms)
2 passing (8s)
I get more with the 50ms but that's only a half second of testing. Some of the other values on the page take longer to update to this is not viable.
I've tried passing the done callback to the it statement but mocha ignores it and doesn't wait for it to be called.
Is this a limitation of the tools or am I using them wrong?
I have tried using the done callback using the method below.
it('has elements whose values update', function (done) {
and
expect(_.uniq(values).length).to.be.gt(1);
done();
It still ignores that I've marked the test as async. At 500ms it still passes without getting to the if statement or done call. At 50ms it throws this error:
done() called multiple times
I am using mocha-casperjs. Could this be affecting it?
It seems that mocha-casperjs doesn't use the default done. It knows that the test step is complete because it uses CasperJS' control flow. In your case, you break out of the control flow by calling getValue through a setInterval.
It would be better to refactor your code to use recursive calls to getValue like this:
function getValue () {
// Grab element's text value
var value = this.evaluate(function () { return $('#element').text(); });
// Push in to our test array
values.push(value);
// 20 * 500ms == 10 seconds or 10000ms
// When set to 500ms, this block never runs. The test passes before it has a chance to
if (values.length === 20) {
// Test to see we've had more than one unique value in the 10 seconds
expect(_.uniq(values).length).to.be.gt(1);
} else {
this.wait(500, getValue);
}
}
// Wait for the value on the page to populate
// It defaults to '-' when the page loads
casper.waitFor(function () {
return this.evaluate(function () {
return $('#element').text() !== '-';
});
// Start the check with a delay of 500ms between each check
}, function then() {
this.wait(500, getValue);
});
This makes getValue a casper step.
Another solution without much refactoring is letting a second waitFor run along side of the broken control flow. This needs a semi-global variable someGlobalVariable. Maybe the intervalId can be used for this, but it's probably better to use someGlobalVariable = false; at the top.
intervalId = setInterval(getValue, 500);
this.waitFor(function check(){
return someGlobalVariable;
}, function then(){
// do something else
}, null, 20000);
and let it stop with
expect(_.uniq(values).length).to.be.gt(1);
someGlobalVariable = true;

Javascript are these calls the same in Node.js?

I'm wondering if these two blocks of code are the same in Node.js?
// Style 1
setTimeout(function () {
console.log('hello');
}, 0);
// Style 2
console.log('hello');
Since above I'm passing 0 for the timeout, there should be no waiting time. Is it identical to just calling console.log('hello'); directly without using setTimeout?
They are different, the first adds the function to the event queue, so that it can execute as soon as it gets a chance after the current execution path completes. The second will execute it immediately.
For example:
console.log('first');
setTimeout(function(){
console.log('third');
}, 0);
console.log('second');
The order that those are printed in is well defined, you could even do something slow (but synchronous) before printing 'second'. It's guaranteed that console.log('second'); will still execute before the callback to setTimeout does:
console.log('first');
setTimeout(function () {
console.log('third'); // Prints after 8 seconds
}, 0);
// Spinlock for 3 seconds
(function(start){ while(new Date - start < 3000); })(new Date);
console.log('second'); // Prints after 3 seconds, but still before 'third'
// Spinlock for 5 seconds
(function(start){ while(new Date - start < 5000); })(new Date);
Strictly speaking they are not exactly the same - setTimeout gives the browser a chance to "catch up" with any tasks that it desperately needs to do. But as far as we're normally concerned, 99% of the time they will do the same thing.

ajax inside an inner forEach loop, finished after the inside callback fires

I have an ajax call that is inside a forEach loop which is inside another function.
The problem is, that the callback of the outer function fires before the inner loop ends - so "staticItemList" is not filled with the items when passed to the callback.
How can I fix that ? I really spent a lot of hours on that. Thanks.
exports.buildTheList = function (parsedList, callback) {
var staticItemList = {};
parsedList.forEach(function(parsedItem) {
db.staticList.find({"_id":parsedItem.ID}).forEach(function(err, doc) {
if (!doc){
console.log("newStaticItem not found in db");
parsedDataFetcher.fetchParsedDetails(Path, function(parsedDetails){
staticItemList["_id"] = parsedItem.ID;
staticItemList[parsedItem.ID] = {
"_id": parsedItem.ID,
"type": parsedItem.type,
}
})
}else {
console.log("newStaticItem already found in db");
}
});
});
callback(staticItemList);
}
Add a counter variable inside the loop, and decrement it every time the async methods complete. Once the counter hits zero, call callback. In pseudo-code:
var counter = 0;
foreach(function() {
counter++;
doAsync(function() {
// add to list
counter--;
if (counter === 0) {
callback(list);
}
});
}
Hope that helps.
I'd look at something like dojo promise all. From the manual:
dojo/promise/all is a function that takes multiple promises and
returns a new promise that is fulfilled when all promises have been
fulfilled.
The examples on that page demonstrate how you can do what you need.
AJAX occurs asynchronously, so what happens is your loops runs, triggering all the AJAX calls, and then completes, calling your callback. You will likely need to implement some sort of counter in your foreach loop and check that on completion of each AJAX call. one the number of completed AJAX calls is the same as the number of the counter, you would execute your callback function from within the asynchronous function context.

Categories