2

I saw that this question has already been asked previously, but I was not sure of a solution to my problem.

I have the following code:

function mouseup (  )
{
    for( i = 0; i < 6; i++ )
    {
        setTimeout(function(){
            alert( "test" );
        },1000);
    } 
}

But when I run the code, after a one second pause, the dialog box comes up (as it should), but when I click OK on that dialog box, the second immediately comes up, without any delay.

jsFiddle example

Is there an easy way to fix this without changing too much of the code? The reason I ask is because I am building a converter that changes code such as:

repeat 6 times
    wait for 1 second
    answer "test"
end repeat

into the JavaScript code above, and do not want to change too much of my converter program if I do not have too.

Many thanks in advance.

Edit: (and if you are wondering, the non-JavaScript code is HyperTalk/xTalk code)

user2370460
  • 7,470
  • 10
  • 31
  • 46
  • 1
    Your code is asking to display 6 alert boxes all at roughly the same time 1 second from now. – CoderDennis Apr 04 '14 at 14:20
  • Your loops executes in microseconds, thus the alerts will all happen 1 second (+ a couple of microsends after) – cloakedninjas Apr 04 '14 at 14:20
  • There is no `wait` or `sleep` in js. Depending on what you exactly want to achieve there are different ways to solve this. – t.niese Apr 04 '14 at 14:20
  • possible duplicate of [Python while loop conversion to Javascript](http://stackoverflow.com/questions/14327647/python-while-loop-conversion-to-javascript) – Bergi Apr 04 '14 at 14:21

8 Answers8

8
window.counter = 6;
function mouseup (  )
{
    setTimeout(function(){
        alert( "test" );
        if ( window.counter-- ) {
            mouseup (  );
        }
    },1000);
}
blue
  • 1,939
  • 1
  • 11
  • 8
7

You can't achieve what you want like that with a for loop. The for loop won't wait for the timeout before continuing. To do what you want, you will need to chain your timeouts together so that ending one timeout starts the next one:

var i = 0;
function TimeoutFired() {
    i++;
    alert( "test" );
    if (i < 6) {
        setTimeout(TimeoutFired, 1000);
    }
}
setTimeout(TimeoutFired, 1000);

http://jsfiddle.net/M98ZL/

Matt Burland
  • 44,552
  • 18
  • 99
  • 171
  • 3
    I prefer this answer to the other answer getting votes because it uses a locally scoped variable as opposed to attaching a property with a very common name to `window` where it could easily be overwritten by accident. – rescuecreative Apr 04 '14 at 14:30
2

As onother solution-way you can use setInterval and after it executes 6 times clear it

function mouseup (  )
{
    var i=0;
    var myVar = setInterval(function(){

        alert("test")
        i++;
        if(i===6){clearInterval(myVar);}                      
    },1000);

}

DEMO

laaposto
  • 11,835
  • 15
  • 54
  • 71
1
function mouseup (  )
{
    var delay = 0;
    for( i = 0; i < 6; i++ )
    {
        setTimeout(function(){
            alert( "test" );
        },(delay += 1000));
    } 
}
Gio
  • 841
  • 1
  • 5
  • 11
  • 4
    What will happen if the user leaves the first alert opened for, lets say 6 seconds? – t.niese Apr 04 '14 at 14:22
  • 1
    @Gio: Looking at the code they are converting, I would assume the later. – Matt Burland Apr 04 '14 at 14:28
  • @t.niese that wouldn't change anything, as _alert_ doesn't only block the code execution, but also pauses any active timeouts. – Gio Apr 04 '14 at 14:51
  • @Gio that is implementation dependent. Chrome will pause the timer while the alter is opened, FireFox won't pause it and in IE it even could (while imho not in this particular case) display multiple alerts the same time. It is true that this is a particular problem with `alert`, `confirm` and `prompt` but even so, this way of solving the problem is not the best one. – t.niese Apr 04 '14 at 15:13
1

While there are already some solutions here i would like to show some other way to solve. (This code does not test for error, it is just that you get an idea what you could do)

 function loop( from, to, callback, finished ) {
   var currIdx = from-1;

   function next() {
     currIdx++;
     if( currIdx < to ) {
       callback(currIdx, next, stop);
     } else {
       if( finished ) {
         finished();
       }
     }
   }

   function stop() {
     if( finished ) {
       finished();
     }
   }
   next();


 }


 loop( 0, 6, function( i, next, stop ) {
   console.log(i);

   setTimeout(function() {
     next();
   }, 1000);

 });

This would allow you to pass the next and/or stop callback to other function allowing you to create loops with async code in an easy way. For more complex things i would suggest to also look at a promise library like whenjs.

t.niese
  • 39,256
  • 9
  • 74
  • 101
0
You can do this in three different ways


1. with IIFE
------------

let fruits = ["banana", "apple", "Orange"];

for (let index = 0; index < fruits.length; index++) {
  (function(fruits, index) {
    setTimeout(() => {
      console.log(fruits[index]);
    }, index * 1000);
  })(fruits, index);
}



2. with Closure, as it reference the outer scope variable even after 
      the main function execution
----------------------------------

let fruits = ["banana", "apple", "Orange"];
function closerWithsettimeout() {
    return function() {
        fruits.forEach(function(elem, i) {
            setTimeout(() => {
                console.log(elem);
            }, 1000 * i);
        });
    };
}
closerWithsettimeout()();



3. With promise
----------------
let fruits = ["banana", "apple", "Orange"];

fruits.forEach((elem, i) => {
  var p = new Promise((resolve, reject) => {
    fruits[0] === "banana" ?
      setTimeout(() => {
        resolve(elem);
      }, i * 1000) :
      reject(new Error("Fruits cannot be bought"));
  });
  p.then(result => console.log(result)).catch(err => console.log(err.message));
});
Mohd Sahil
  • 385
  • 3
  • 3
0

This question now already has multiple good answers but since none of them mentions async-await, I'd like to present a solution using it. If your goal is to make as few changes as possible to your original code, it doesn't get better than this.

async function mouseup (  )
{
    for( i = 0; i < 6; i++ )
    {
        await new Promise(function(resolve) {
            setTimeout(function(){
                alert( "test" );
                resolve();
            },1000);
        });
    } 
}
Lennholm
  • 7,205
  • 1
  • 21
  • 30
0

This issue is caused by HyperTalk letting you write script commands to be executed sequentially, whilst JavaScript only fires them sequentially.

Since ES7, JavaScript can now let you use async/await to execute code sequentially.

An example conversion:

// Procedure to invoke upon mouseUp //
async function mouseup() {
    for( i = 0; i < 6; i++ ) {
        await myscript();
    } 
}

// Script to be invoked inside the for loop //
async function myscript() {
    setTimeout(function(){
        alert( "test" );
    },1000);
}

// Note: this does not include error trapping

Note, with JavaScript, you would need to trap the mouseUp event (there is no equivalent to just putting it in the object's script to trigger via an inbuilt trap, like in HyperCard).

Previous JavaScript practice was to reference a single event trigger in the DOM element (e.g. button), but current practice is to trap it in a general listener instead. There are a few ways to do this (e.g. listen for 'click' event from the element ID, or listen for a bubble to a more senior item and filter for only those from your chosen element).

The syntax for listening to the JavaScript click or (mouseup if you prefer) can be found at https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener

ed2
  • 1,457
  • 1
  • 9
  • 26