Michael Durrant
Michael Durrant

Reputation: 96544

How can I slow down this js with setTimeout?

I want to slowly rotate the letters in a string using 2 second pauses.

I'm trying to use setTimeout(function, delay) but it doesn't pause it runs the entire script in under a second. How can I do this?

My code is at https://jsfiddle.net/6un4xhuj/

<html>
<head>
<link type="text/css" href="style.css">
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<h1>JS</h1>
<h2 id='for'>For: String Rotations</h2>
<h3 id='result'>Result:</h3>
<h4></h4>
<span id="end_of_results"></span>
<script src="main.js"></script>
</body>
</html>

js:

'use strict';
function display(string) {
  $("span#end_of_results").before( $( "<h4>" + string + "</h4>" ) );
}
var string="rotate me";
for (var i = 0; i <= string.length; i++) {
  setTimeout(display(string), 2000);
  string=string[0,string.length - 1] + string.slice(0,-1);
}

I get this output but all of it immediately, not step by step.

enter image description here

Upvotes: 1

Views: 1467

Answers (6)

vahanpwns
vahanpwns

Reputation: 963

You are beginning the 2 second wait immediately for all instances of setTimeout. The simplest way to fix your code is to increase the wait each time you go through your loop:

change this:

setTimeout(display(string), 2000);

to this:

setTimeout(display(string), (i+1)*2000);

Alternatively you could make a single HTML element update on a 2 second interval using setInterval() like this:

<div class="rotating-text"> rotate me</div>
<script>
  setInterval(function(){
    $('.rotating-text').each(function(){
      var string = $(this).text();
      string = string[0,string.length - 1] + string.slice(0,-1);
      $(this).text(string);
    });
  }, 2000);
</script>

This example applies to everything with a class rotating-text

http://codepen.io/t3hpwninat0r/pen/eZVYqw

Upvotes: 1

Michael Durrant
Michael Durrant

Reputation: 96544

I've accepted andy's answer, also based on it, I create this version to only scroll once:

<h1>JS</h1>
<h2 id='for'>For: String Rotation</h2>
<h2 id='result'>Result:</h2>

'use strict';

function rotate(string) {
  string = string[0, string.length - 1] + string.slice(0,-1);
  $("h2#result").html( $( "<h3>" + string + "</h3>" ) ).animate(100);
  rotations+=1
  if (rotations > string.length) {
    return;
  }
  setTimeout(rotate, 50, string);
}

var string="rotate this string ";
var rotations=0
rotate(string);

fiddle at https://jsfiddle.net/xb865yw6/3/

Upvotes: 1

guest271314
guest271314

Reputation: 1

You can use .queue() , .delay()

"use strict";

var elem = $("h2#result");

var string = "rotate this string";

elem.append("<br>" + string)
.queue("_fx", $.map(Array(string.length), function(value) {
  return function(next) {
    string = string[0,string.length - 1] + string.slice(0,-1);
    $(this).delay(2000).queue(function() {
      $(this).append("<br>" + string).dequeue();
      next()
    })
  }
})).dequeue("_fx")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<h1>JS</h1>
<h2 id='for'>For: String Rotation</h2>
<h2 id='result'>Result:</h2>

Upvotes: 0

Andy
Andy

Reputation: 63550

Get rid of the loop and just recurse the same function passing in the string each time. This code repeats without end so you might want to add a condition as per your requirements.

function thing(string) {
  string = string[0, string.length - 1] + string.slice(0,-1);
  $("h2#result").html( $( "<h3>" + string + "</h3>" ) );
  setTimeout(thing, 2000, string);
}

thing(string);

DEMO

Upvotes: 5

carlosdubusm
carlosdubusm

Reputation: 1083

setTimeout is async, which means the for loop is completing immediately. Also... you are calling display directly instead of passing the function reference. The same algorithm using callbacks would be like this:

jsfiddle: https://jsfiddle.net/w1x0csp9/

'use strict';
var i = 0;
var string="rotate me";
function display() {
  if(i > string.length) {
    return;
  }
  $("span#end_of_results").before( $( "<h4>" + string + "</h4>" ) );
  string = string[0,string.length - 1] + string.slice(0,-1);
  i++;
  setTimeout(display, 2000);
}

display();

Upvotes: 1

lxe
lxe

Reputation: 7599

setTimeout takes a function in your argument. In your code setTimeout(display(string), 2000);, you pass the result of invoking the function display(string) into setTimeout. To make this more evident, assign it to a variable like this:

var result = display(string); // undefined
setTimeout(result, 2000); // Not right

You'll need to use something like bind, or just create a function that returns a function:

setTimeout(function () {
  // Modify your string here
  string=string[0,string.length - 1] + string.slice(0,-1);
  display(string)
}, 2000);

Upvotes: 2

Related Questions