How to setInterval Generator correctly? - javascript

Goal:
I want to create a generator function that is being invoked inside setInterval(), and console.log 1 to 10.
the problem:
In order to clearInterval() at the end I need a condition to check if gen.next().done === true.
but every time the condition runs it actualy calls another .next()
so so final print i get is:
1 3 5 7 9 undefined
How do I set a done == true condition without calling .next() ?
function* myGen(){
let counter = 0;
for(let i = 0 ; i <= 10; i++){
yield counter++;
}
}
const gen = myGen();
const start = setInterval(() => {
if(gen.next().done){
clearInterval(start);
} else {
console.log(gen.next().value);
}
}, 1500)

You remember the object in a variable rather than calling next a second time:
function* myGen(){
let counter = 0;
for(let i = 0 ; i <= 10; i++){
yield counter++;
}
}
const gen = myGen();
const start = setInterval(() => {
var next = gen.next(); // *** Save it here
if(next.done){ // *** Use it here...
clearInterval(start);
} else {
console.log(next.value); // *** ...and here
}
}, 150)

You can alternatively use for..of loop, setTimeout(), async/await to avoid the need to check for .done property value
function* myGen() {
let counter = 0;
for (let i = 0; i <= 10; i++) {
yield counter++;
}
}
const gen = myGen();
(async() => {
for (let n of gen) {
await new Promise(resolve => {
setTimeout(() => {
console.log(n);
resolve()
}, 1500)
})
}
})();

Another way is to use the relatively new AsyncGenerator feature
https://github.com/tc39/proposal-async-iteration
I think it abstracts the problem nicely (creating an iterator that sleeps in between each iteration).
async function* sleepGenerator(numSleeps, sleepMillis) {
for (let i = 0; i < numSleeps; i++) {
await sleep(sleepMillis);
yield {i, numSleeps, sleepMillis};
}
}
function sleep(sleepMillis) {
return new Promise(resolve => setTimeout(resolve, sleepMillis));
}
(async function run() {
for await (const iterMeta of sleepGenerator(5, 500)) {
console.log(iterMeta);
}
})();

just, store the nextValue
function* myGen(){
let counter = 0;
for(let i = 0 ; i <= 10; i++){
yield counter++;
}
}
const gen = myGen();
const start = setInterval(() => {
let nextValue = gen.next();
if(nextValue.done){
clearInterval(start);
} else {
console.log(nextValue.value);
}
}, 1500)

function* myGen(){
let counter = 0;
for(let i = 0 ; i <= 10; i++){
yield counter++;
}
}
const gen = myGen();
const start = setInterval(() => {
var genObj=gen.next();//keep next result as an object to avoid use next method twice
if(genObj.done){
clearInterval(start);
} else {
console.log(genObj.value);
}
}, 1500)//I spent an hour learning this,late but get some konwledge,so,thanks.

Related

Node.js: How to prevent the next line execution until my forLoop gives me result

let a = []
for (let i=0;i<4;i++){
setTimeout(()=>{
a.push(i);
},2000)
}
console.log(a);
Here I am getting a always blank []. Please suggest a way to stop executing console.log(a) line until a gets filled.
You should await setTimeout:
let a = [];
async function test() {
for (let i = 0; i < 4; i++) {
await new Promise((resolve) =>
setTimeout(() => {
a.push(i);
resolve();
}, 2000)
);
}
}
await test();
console.log(a);
Note: You can also use Promise.allSettled to run them at the same time

how can i execute the loop again after loop finish using javascript

In my code i want to loop again once a certain condition if(i === arr.length) matched. here is the demo code. any idea how can i solve it?
var arr = [1,2,3];
for (let i = 0; i < arr.length; i++)
{
setTimeout( function timer()
{
console.log("test");
if(i === arr.length){
// start the looping after this condition match
}
}, i*3000 );
}
Your code never reaches the condition since i < arr.length is your exit,
if you change it to i <= arr.length then the condition will be reached,
then to re-enter the loop i suggest that you encapsulate all your loop in a function
var arr = [1,2,3];
for (let i = 0; i < arr.length; i++)
{
setTimeout( function timer()
{
console.log("test");
if(i === arr.length){
// start the looping after this condition match
}
}, i*3000 );
}
To
var arr = [1,2,3];
function Looper(){
for (let i = 0; i <= arr.length; i++)
{
setTimeout( function timer()
{
console.log("test");
if(i === arr.length){
Looper();
}
}, i*3000 );
}
}
Looper();
Keep in mind that this is an infinite loop, you would better use setInterval, if thats your goal, but if you have something else in mind hope this helps.
The problem with your code as it is now is the loop is exited before even the first setTimeout call back runs. The addition of a console.log after the loop confirms this.
var arr = [1,2,3];
for (let i = 0; i < arr.length; i++)
{
setTimeout( function timer()
{
console.log("test");
if(i === arr.length){
// start the looping after this condition match
}
}, i*3000 );
}
console.log("Loop exited");
So there is no way to restart that loop. However you can re-organise your code to keep looping over your arr with a 3000ms delay. The easiest is probably with a Promise.
var arr = [1,2,3];
async function doWork(item){
return new Promise(resolve => {
setTimeout(() => {
console.log("finished",item); // do whatever you want to do here
resolve();
},3000);
});
}
async function test(){
var exit = false; // if you set this to true elsewhere the code will stop
var i = 0;
while(!exit){
var item = arr[i++ % arr.length];
await doWork(item);
}
}
test();
Here is simple attempt hope you'll find it helpful
var arr = [1, 2, 3];
var i = 0;
var i = 0;
function timer() {
console.log(arr[i])
i = (i + 1) % arr.length;
}
setInterval(timer, 1000);

How to set a timeout between each iteration in Javascript?

I am trying to set a timeout(wait time) between each call of drawLines() inside a setTimeout() in a while-loop.
while(i < j){
while(i < lastElIndex && array[i] < pivot){
const iCopy = i;
const lastCopy = lastElIndex;
i++;
const arrCopy = array.slice();
setTimeout(() => {
drawLines(arrCopy, iCopy, -1, lastCopy, -1);
});
}
while(j > firstElIndex && array[j] >= pivot){
const jCopy = j;
const firstCopy = firstElIndex;
j--;
const arrCopy = array.slice();
setTimeout(() => {
drawLines(arrCopy, -1, jCopy,-1, firstCopy);
});
}
I already tried setting the wait time like this
setTimeout(() => {drawLines(arrCopy, iCopy, -1, lastCopy, -1);}, 1000);
...but the timeout happens before the calling drawLines() repeatedly without waiting between calls.
I also tried
setTimeout(async() => {
drawLines(arrCopy, iCopy, -1, lastCopy, -1);
await sleep(1000);
});
...with
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
...but nothing happened
Here is the whole function:
function quickSort(arr, firstElIndex, lastElIndex){
let array = arr;
let currentIndex = 0;
function partition(firstElIndex, lastElIndex) {
let i = firstElIndex;
let j = lastElIndex;
let pivot = array[lastElIndex];
while(i < j){
while(i < lastElIndex && array[i] < pivot){
const iCopy = i;
const lastCopy = lastElIndex;
i++;
const arrCopy = array.slice();
setTimeout(() => {
drawLines(arrCopy, iCopy, -1, lastCopy, -1);
});
}
while(j > firstElIndex && array[j] >= pivot){
const jCopy = j;
const firstCopy = firstElIndex;
j--;
const arrCopy = array.slice();
setTimeout(() => {
drawLines(arrCopy, -1, jCopy,-1, firstCopy);
});
}
if(i < j){
array.swap(i, j);
}
}
if(array[i] > pivot){
array.swap(i, lastElIndex);
}
return i;
}
if(firstElIndex < lastElIndex){
currentIndex = partition(firstElIndex, lastElIndex);
quickSort(array, firstElIndex, currentIndex - 1);
quickSort(array, currentIndex + 1, lastElIndex);
}
setTimeout(() => {drawLines(array, -1, -1);}, 1000);
}
And here is drawLines():
function drawLines(arr, x, y, rightPivot, leftPivot){
let array = arr.slice();
let container = document.getElementById("centerContent");
let line = undefined;
container.innerHTML = '';
for(let i = 0; i < array.length; i++){
let my_length = (array[i]/6.7).toString() + "vw";line = document.createElement("div");
line.className = "line";
if(i === x || i === y){
line.style.borderLeft = (1/7).toString() + "vw solid red";
}
else if(i === rightPivot){
line.style.borderLeft = (1/7).toString() + "vw solid aqua";
}
else if(i === leftPivot){
line.style.borderLeft = (1/7).toString() + "vw solid orange";
}
else{
line.style.borderLeft = (1/7).toString() + "vw solid black";
}
line.style.height = my_length;
container.appendChild(line);
}
}
I apoligize if the question is poorly structured. It's the my first one. Many thanks in advance.
It sounds like you just want a delay to happen before the line is drawn and also before the loop continues. Using async/await can do that for you fairly easily. This is a structure you can use:
// to use await you must define the function to be async
async function partition(firstElIndex, lastElIndex) {
let i = firstElIndex;
let j = lastElIndex;
let pivot = array[lastElIndex];
while(i < j){
while(i < lastElIndex && array[i] < pivot){
const iCopy = i;
const lastCopy = lastElIndex;
i++;
const arrCopy = array.slice();
// this line pauses execution for 1 second
await new Promise(resolve => setTimeout(resolve, 1000);
drawLines(arrCopy, iCopy, -1, lastCopy, -1);
}
while(j > firstElIndex && array[j] >= pivot){
const jCopy = j;
const firstCopy = firstElIndex;
j--;
const arrCopy = array.slice();
// this line pauses execution for 1 second
await new Promise(resolve => setTimeout(resolve, 1000);
drawLines(arrCopy, -1, jCopy,-1, firstCopy);
}
if(i < j){
array.swap(i, j);
}
return i;
}
}
and since partition is now an async function you need to do this when calling it:
if(firstElIndex < lastElIndex){
currentIndex = await partition(firstElIndex, lastElIndex);
quickSort(array, firstElIndex, currentIndex - 1);
quickSort(array, currentIndex + 1, lastElIndex);
}
That requires that quickSort is also and async function which means it returns a Promise object that can be either waited for too, or use .then to know when it's complete.
The first thing is that the setTimeout needs a 2nd parameter with the amount of time to wait, so written like this:
setTimeout(() => {
drawLines(arrCopy, iCopy, -1, lastCopy, -1);
});
will not wait for any amount of time, unless you pass it a 2nd argument like:
setTimeout(() => {
drawLines(arrCopy, iCopy, -1, lastCopy, -1);
}, 500);
I think you where on the correct path when creating that sleep function; indeed it wouldn't work when running the timeout inside the loop. But there's no need to return a Promise. Just try:
function sleep(callback, ...callbackParameters) {
setTimeout(() => callback(...callbackParameters), 500) // remember your timer
}
And then pass whatever function you want to run after the timeout to your sleep function like:
sleep(drawLines, arrCopy, iCopy, -1, lastCopy, -1)

change the number of the iterator in a for loop

I want to conditionally break out of a loop like this..
for(let i = 0; i < 3; i++) {
exampleFunction().then(result => {
res = 0 ? i = 3 : null
})
}
I want exampleFunction to run at least 3 times unless it gets the desired result, in which case I want it to stop running.
exampleFunction runs asynchronously. The only way to get it working is using async/await.
const iterateWithExampleFunction = async () => {
for (let i = 0; i < 3; i++) {
console.log('before', i)
await exampleFunction().then(result => {
i = result === 0 ? 3: i;
});
console.log('after', i)
}
};
const exampleFunction = async () => {
return 0;
}
iterateWithExampleFunction();
You can have a count on the outer scope and then do the async call.
let count = 0;
function executeCall() {
exampleFunction().then(result => {
// do something with the result
if (result !== 0 && count !== 3) {
count += 1;
executeCall();
}
});
}
Just await the result, than break:
(async function() {
for(let i = 0; i < 3; i++) {
const result = await exampleFunction();
if(result === 0) break;
}
})();
Hopefully this gives you some ideas
async function poll(f, threshold = 3) {
if (!threshold) {
throw new Error("Value not found within configured amount of retries");
}
const result = await f();
if (result >= 0.5) {
return result;
} else {
return await poll(f, threshold - 1);
}
}
async function task() {
return Math.random();
}
poll(task).then(console.log).catch(console.error);

How to make for loop log line by line with 1sec delay in JS

My code will log all 10 lines in the order that I want (descending triangle), but I need it to delay 1 second before logging each successive line. I tried putting a setTimeout before the for loop, but that just caused a 1 second delay before printing all 10 lines concurrently.
function minusTen(num) {
var arr = '';
for (var i = num; i > 0; i--) {
arr += '*';
}
var newArr = arr.split('');
for (var j = num; j > 0; j--) {
newArr.pop();
console.log(newArr.join(' '));
}
}
minusTen(10);
I can use jQuery but I'd like to avoid having to implement Bootstrap if possible.
Thank you!
you can use setTimeout for it but then you will have to keep setTimeout inside the for loop. you can also use setInterval here and clear the interval if num becomes 0. something like this:
function minusTen(num) {
var arr = '';
for (var i = num; i > 0; i--) {
arr += '*';
}
var newArr = arr.split('');
var interval = setInterval(function(){
newArr.pop();
console.log(newArr.join(' '));
num--;
if(!num)
clearInterval(interval);
}, 1000)
}
minusTen(10);
You can use a function. Check the .length of newArr, if greater than 0, call function again
function minusTen(num) {
var arr = '';
for (var i = num; i > 0; i--) {
arr += '*';
}
var newArr = arr.split('');
function fn() {
if (newArr.length)
setTimeout(function() {
console.log(newArr.join(" "));
newArr.pop();
fn()
}, 1000)
else
console.log("done, newArr.length:", newArr.length);
}
fn()
}
minusTen(10);
function minusTen(num) {
var arr = '';
for (var i = num; i > 0; i--) {
arr += '*';
}
var newArr = arr.split('');
function printLine(counter){
var k = counter;
window.setTimeout(function(){
console.log(newArr.join(' '));
newArr.pop();
}, k*1000);
console.log(k);
}
for (var j = num; j > 0; j--) {
printLine(j);
}
}
minusTen(10);
It's straightforward with async/await. delay holds the execution of the code for a specified amount of time.
async function minusTen(num) {
var arr = '';
for (var i = num; i > 0; i--) {
arr += '*';
}
var newArr = arr.split('');
for (var j = num; j > 0; j--) {
newArr.pop();
await delay(1000)
console.log(newArr.join(' '));
}
}
function delay(time) {
return new Promise((resolve) => setTimeout(resolve, time))
}
minusTen(10);
You can use setTimeout with an offset. Just add whatever parameters you need to the log function.
function log(offSet) {
setTimeout(() => {
const str = new Array(offSet + 1).join('*');
console.log(str);
}, 1000 * offSet);
}
for(let i = 1; i < 11; i ++) {
log(i);
}
https://jsfiddle.net/ycreaL9w/
If you wanted to reduce your code footprint a little you could do something like this:
const printStars = (n) => {
// Make sure we don't try to print zero stars
if (n > 0) {
// Fill an array with n stars
const arr = Array(n).fill('*').join(' ');
console.log(arr);
// After a second, reduce n by 1 and call
// the function again
setTimeout(() => printStars(--n), 1000);
}
}
printStars(10);
DEMO
It's worth pointing out here, however, that IE/Opera don't support Array.fill.
If you need to support them use an for/loop like in your example. Here I've separated out that code into its own function.
const getStars = (n) => {
let arr = [];
for (let i = 0; i < n; i++) {
arr.push('*');
}
return arr.join(' ');
}
const printStars = (n) => {
if (n > 0) {
console.log(getStars(n));
setTimeout(() => printStars(--n), 1000);
}
}
DEMO 2

Categories