Question

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)

Was it helpful?

Solution

window.counter = 6;
function mouseup (  )
{
    setTimeout(function(){
        alert( "test" );
        if ( window.counter-- ) {
            mouseup (  );
        }
    },1000);
}

OTHER TIPS

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/

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

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

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.

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

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top