This question already has answers here:
Why is the method executed immediately when I use setTimeout?
(8 answers)
Calling functions with setTimeout()
(6 answers)
Combination of async function + await + setTimeout
(17 answers)
Closed 7 months ago.
I have a program which is supposed to demonstrate a bubble sort, however I want the program to wait one second after every swap to demonstrate what swap is being made. Attached is my code:
function bubbleSort(arr){
for(var i = 0; i < arr.length-1; i++){
for(var j = 0; j < arr.length-i-1; j++){
if(arr[j] > arr[j+1]){
swap(arr,j,j+1);
}
}
}
}
Here is my swap function:
async function swap (arr,a,b){
var temp = arr[a];
var temp2 = arr;
temp2[a] = arr[b];
temp2[b] = temp;
await setTimeout(updateText(),1000);
return temp2;
}
And here is updateText:
function updateText(arrayText){
document.getElementById('arrayText').innerHTML = printArray(arr);
}
Whenever I execute the bubbleSort() function, my array goes from unsorted to sorted instantly, and does not show the swaps needed to reach the solved array.
updateText() is not called anywhere else in my program except for in the swap function.
Any help would be greatly appreciated and I can provide clarifications if needed.
The issue is the setTimeout function does not return a promise, which await expects.
function resolveAfter1Second() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 1000);
});
}
async function swap (arr,a,b){
var temp = arr[a];
var temp2 = arr;
temp2[a] = arr[b];
temp2[b] = temp;
await resolveAfter1Second();
return temp2;
}
Here's an example that simply logs the array to the console. You can replace the console.log(arr) line with a web page update, as needed.
const waitFor = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
async function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
await swap(arr, j, j + 1);
}
}
}
}
async function swap(arr, a, b) {
[arr[a], arr[b]] = [arr[b], arr[a]]
console.log(arr);
return waitFor(1000);
}
const array = [10, 9, 4, 5, 3, 1, 2];
bubbleSort(array);
Related
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);
In my JS script, I am trying to index thru silnia() a function that returns an array, I can do that manually without a problem: silnia(5)[1] but when I try to use an i from a for-loop it does not work.
koniec = [1,2,3];
for (i = 0; i < koniec.length; i++){
// Returns only undefined:
console.log(silnia(5)[i]);
// Works no problem:
// console.log(silnia(5)[2]);
}
function silnia(n){
var wynikSilni = [];
for(i = 1; i < (n + 1); i++){
wynikSilni.push(i);
}
return wynikSilni;
}
You're not using a var, let or const statement to declare i, so it is considered a global variable.
Which means the same i you use in the silnia function is the same i being used in the for loop outside of it; essentially, the loop outside of it runs once, the silnia increments i to 6, and once it returns to the for loop in global scope, it stops because i>koniec.length (ETA: It then tries to access sylnia(5)[6] because i equals 6 at that point in time, which is undefined)
Try this:
function silnia(n) {
var wynikSilni = [];
for (var i = 1; i < (n + 1); i++) {
wynikSilni.push(i);
}
return wynikSilni;
}
koniec = [1, 2, 3];
for (var i = 0; i < koniec.length; i++) {
// Returns only undefined:
console.log(silnia(5)[i]);
// Works no problem:
// console.log(silnia(5)[2]);
}
It's 2019 and Arrays have quite a few helpful methods that eliminate the need to set up and manage loop counters, which as others have pointed out, is the source of your problem.
Array.forEach() is the simplest of these and will help to greatly simplify your issue:
koniec = [1,2,3];
// Loop over the knoiec array
// .forEach requires a callback function to execute
// upon each loop iteration. That function will automatically
// be passed 3 arguments: the array item, the item index, the array
koniec.forEach(function(item, index){
console.log(silnia(5)[index]);
});
function silnia(n){
var wynikSilni = [];
for(i = 1; i < (n + 1); i++){
wynikSilni.push(i);
}
return wynikSilni;
}
You need to declare the variables, otherwise you use global variables for all functions.
function silnia(n) {
var wynikSilni = [];
for (var i = 1; i < (n + 1); i++) { // use var or let
wynikSilni.push(i);
}
return wynikSilni;
}
var koniec = [1, 2, 3];
for (var i = 0; i < koniec.length; i++) { // use var or let
console.log(silnia(5)[i]);
}
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
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 5 years ago.
I am pretty new level to JavaScript.
I have a function taking y as the input to return an array with size y which has almost the same functions as the elements.
This is my code:
function createArrayOfFunctions(y) {
var arr = [];
for(var i = 0; i<y; i++) {
arr[i] = function(x) {
return x + i);} //Probably this is the issue
}
return arr;
}
var input = "5,3,2";
var [y, n, m] = input.split(",");
console.log("input = " + input);
[y, n, m] = [parseInt(y), parseInt(n), parseInt(m)]
var addArray = createArrayOfFunctions(y);
console.log(addArray[n](m)); //I would like to add up n and m
When I execute the code, the i keeps at 5 instead of iterating from 0,1,2,3,4 for arr[0],arr[1],arr[2],arr[3],arr[4]. As a result, it always output m+y instead of n+m. I have tried to use indexOF and this[i] to figure out the index of the functions, but it does not work. Is there any ways for me to do this?
Thanks!
You've encountered a scoping issue.
Here is a solution:
function createArrayOfFunctions(y) {
let arr = [];
for (let i = 0; i < y; i++) {
arr[i] = function(x) {
return x + i;
};
}
return arr;
}
The above uses let, which scopes the i variable to each loop iteration.
If you can't use let due to compatibility, use an IIFE for each loop:
function createArrayOfFunctions(y) {
var arr = [];
for (var i = 0; i < y; i++) {
(function() {
var _i = i;
arr[_i] = function(x) {
return x + _i;
};
})();
}
return arr;
}
The above code caches each i variable for each loop in an IIFE, which keeps it in scope.
Based on this example - vo/examples/9-pipeline-composition.js, how would I return yield a promise for each iteration of this for loop?
At the moment the loop runs once and yields a single promise.
function * get (urls) {
for (var i = urls.length - 1; i >= 0; i--) {
console.log(urls[i])
return yield http.get(urls[i])
}
}
function status (res, params) {
return res.status
}
let scrape = vo(get(['http://standupjack.com', 'https://google.com']), status)
vo([ scrape ])
.then(out => console.log('out', out))
.catch(e => console.error(e))
When u do the return inside the for loop, the loop breaks and returns the result and the loop wont move forward. You can call a function inside the for loop which handles the result and not return it.
function * get (urls) {
for (var i = urls.length - 1; i >= 0; i--) {
console.log(urls[i])
let result = yield http.get(urls[i])
yield handleResult(result)
}
}
Orelse u can push each of the results in an array and return all of then together at the end
function * get (urls) {
let resArr = []
for (var i = urls.length - 1; i >= 0; i--) {
console.log(urls[i])
let result = yield http.get(urls[i])
resArr.push(result)
}
return resArr
}