pause in javascript doesn't work in a loop - javascript

This is the sleep function:
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
This is a function for actualize the textarea content:
Luminosidad.prototype.prueba = function(num)
{
document.getElementById("pruebas").value = num;
}
When i want do it in a loop, i can only see the last value.
This is the code:
for (var i = 1; i < 8; i++)
{
sleep(this.opcion.velocidad);
this.prueba(i);
}
On the textarea, i can only see "7", the last number. It is like don't actualize correcly. Why can it happens? (sleep works correctly)

Why can it happens?
Because you never give the browser a chance to show the updates you're making, because you're keeping the UI thread busy looping pointlessly.
Busy-waits are essentially never the right answer to basically any situation.
In this case, what you're looking for is either setInterval of a series of setTimeouts:
function Luminosidad() {}
Luminosidad.prototype.prueba = function(num) {
document.getElementById("pruebas").value = num;
};
Luminosidad.prototype.loop = function(milliseconds) {
var i = 1;
var obj = this;
setTimeout(next, milliseconds);
function next() {
obj.prueba(i++);
if (i < 8) {
setTimeout(next, milliseconds);
}
}
};
var l = new Luminosidad();
l.loop(500);
<input type="text" id="pruebas">
Note: The loop function returns before any updates to the pruebas element. If you have logic that needs to run after all the updates are complete, you'll want to have loop either accept a callback or return a promise.
Since JavaScript doesn't have a standard form of promise yet (they're coming in ES6, so not far away at all), here's an example of having a callback:
// Requires that your browser support ES6 promises!
function Luminosidad() {}
Luminosidad.prototype.prueba = function(num) {
document.getElementById("pruebas").value = num;
};
Luminosidad.prototype.loop = function(milliseconds, callback) {
var i = 1;
var obj = this;
setTimeout(next, milliseconds);
function next() {
obj.prueba(i++);
if (i < 8) {
setTimeout(next, milliseconds);
} else {
callback();
}
}
};
var l = new Luminosidad();
l.loop(500, function() {
// This runs when everything is done
document.body.insertAdjacentHTML(
"beforeend",
"All done"
);
});
<input type="text" id="pruebas">
In ES6, you might use a promise instead:
// Requires that your browser support ES6 promises!
function Luminosidad() {}
Luminosidad.prototype.prueba = function(num) {
document.getElementById("pruebas").value = num;
};
Luminosidad.prototype.loop = function(milliseconds) {
var obj = this;
return new Promise(function(resolve) {
var i = 1;
setTimeout(next, milliseconds);
function next() {
obj.prueba(i++);
if (i < 8) {
setTimeout(next, milliseconds);
} else {
resolve();
}
}
});
};
var l = new Luminosidad();
l.loop(500).then(function() {
// This runs when everything is done
document.body.insertAdjacentHTML(
"beforeend",
"All done"
);
});
<input type="text" id="pruebas">

Related

How to wait for function to stop before calling it again

I have had trouble trying to get my function to finish before calling it again. This is my code now:
function TypeAnimation(str, time)
{
var text = document.getElementById('loading');
for (let i = 0; i < str.length; i++) {
let k = i;
setTimeout(function(){
text.innerHTML += str.charAt(i);
}, time * (k + 1));
}
}
function StartGame()
{
TypeAnimation('first line of text', 50)
TypeAnimation('next line of text but slower', 100)
}
window.onload = StartGame;
When doing this I get the output as finrset xlitne olf itenxte of text but slower.
I have tried promises by using the following code:
function TypeAnimation(str, time)
{
return new Promise((resolve) => {
var text = document.getElementById('loading');
for (let i = 0; i < str.length; i++) {
let k = i;
setTimeout(function(){
text.innerHTML += str.charAt(i);
}, time * (k + 1));
}
resolve();
});
}
function StartGame()
{
TypeAnimation('first line of text', 50).then(TypeAnimation('next line of text but slower', 100))
}
window.onload = StartGame;
But when I do this I still get the same result
On top of this, I also tried the same thing but with with a different StartGame()
async function StartGame()
{
await TypeAnimation('first line of text', 50)
TypeAnimation('next line of text but slower', 100)
}
Once again, the same issue arises and I get the same output
If it helps, this is all created with an electron js app.
Your code doesn't work, because you resolve the promise before timeouts even happen
The approach like this will make it easier
async function TypeAnimation(str, time) {
const text = document.getElementById('loading');
for (let i = 0; i < str.length; i++) {
text.innerHTML += str.charAt(i);
await wait(time)
}
}
async function StartGame() {
await TypeAnimation('first line of text', 50)
await TypeAnimation('next line of text but slower', 100)
}
StartGame()
function wait(time) {
return new Promise(r => setTimeout(r, time))
}
<span id="loading"></span>

JavaScript: How to clear interval after specific duraion with condition executed

I need to run the below code and after 10 seconds the SetInteraval function to be stopped but in the same time to assure that the full word has been executed.
The code I had written:
var word = "I love JS More than any Programming Language in the world!";
var counter = 0;
var autoTyping = setInterval(function() {
var h3 = document.getElementById("myh3");
h3.innerText = word.substring(0, counter);
counter++;
if (counter > word.length) {
counter = 0;
}
}, 100);
setTimeout(function() {
clearInterval(autoTyping);
}, 5000);
So I need after 5 seconds this code stop and this happened but it can be stopped without ensuring that full word "Variable word" has been totally completed written on the DOM.
I assume that you want to print the word variable to h3 element, per letter, and stop it after 5s AND the variable was fully-typed.
Here's my solution with recursive approach:
[UPDATE]
Added typing loop with timeout stopper
// word to type
var _word = "I love JS More than any Programming Language in the world!"
// target element's id
var _target = 'myh3'
// time to fully-typed the word
var _time = 5000 // ms
// speed is depend on _time and _word's length
var _speed = _time/_word.length
// your auto-typing stopper
var _timeout = 10000 // ms
// auto-typing function
function autoType (word, target, speed, timeout) {
var element = document.getElementById(target)
var counter = 0
var stopped = false
function typing(){
if(counter < word.length){
element.innerHTML += word[counter]
counter++
setTimeout(function(){
// recursive call
typing()
}, speed)
}else{
// check if you want it to stop
if(stopped === false){
// ok. you don't want it to stop now. reset counter
counter = 0
// reset the element if you want it too
element.innerHTML = ''
// start it again
typing()
}else{
// console.log('auto-typing is done')
}
}
}
// timeout is required. you dont want a infinite loop, right?
if(timeout){
typing()
setTimeout(function(){
stopped = true
}, timeout)
}
}
// execute it
autoType(_word, _target, _speed, _timeout)
body {background: white}
<h3 id="myh3"></h3>
Well, you are almost there. In your setInterval callback add a line where you clear the interval whenever the word length is reached.
In the setTimeout callback first check if the innerText value of your element is equal to the word. This way you can see if the full sentence has been printed out and only stop if it is so. Otherwise the setInterval will just continue to run until the word length is reached.
var h3 = document.getElementById("myh3");
var word = "I love JS More than any Programming Language in the world!";
var counter = 0;
var autoTyping = setInterval(function() {
h3.innerText = word.substring(0, counter);
counter++;
if (counter >= word.length) {
clearInterval(autoTyping);
counter = 0;
}
}, 100);
setTimeout(function() {
if (h3.innerText === word) {
clearInterval(autoTyping);
}
}, 5000);
you can just have the clearinterval in your if statement, without the need to have setTimeout function:
var word = "I love JS More than any Programming Language in the world!";
var counter = 0;
var h3 = document.getElementById("myh3");
var autoTyping = setInterval(function() {
h3.innerText = word.substring(0, counter);
counter++;
if (counter > word.length) {
counter = 0;
//clearInterval(autoTyping);
}
}, 100);
setTimeout(function() {
if(h3.innerHTML !== word) {
h3.innerHTML = word;
}
clearInterval(autoTyping);
}, 10000);
<div id="myh3">
</div>
I really like to use ES6 generator functions, when it comes to intervals. They make the code much cleaner.
Here's an example of a reusable typewriter function, that takes the element, the word and the interval; and returns a stop function:
function typewriter(element, word, interval){
let stopped = false
const iterator = (function*() {
//This try..finally is not necessary, but ensures that typewriter stops if an error occurs
try{
while(!stopped){
for(let i=0; i<word.length; i++){
element.innerText = word.substring(0, i);
yield
}
}
}finally{
clearTimeout(autoTyping)
}
})()
const autoTyping = setInterval(() => iterator.next(), interval);
iterator.next()
return function stop(){
stopped = true
}
}
const word = "I love JS More than any Programming Language in the world!";
const h3 = document.getElementById("myh3");
const stop1 = typewriter(h3, word, 100)
setTimeout(stop1, 10000)
const secondh3 = document.getElementById("my2ndh3");
const stop2 = typewriter(secondh3, word, 100)
//Even if stopped immediately, it types the full sentence
stop2()
<h3 id="myh3"></h3>
<h3 id="my2ndh3"></h3>
To return results, you can even promisify this function, that has the advantage of having an exception channel for asynchronous errors:
function typewriter(element, word, interval){
let stopped = false
const promise = new Promise((resolve, reject) => {
const iterator = (function*() {
try{
while(!stopped){
for(let i=0; i<word.length; i++){
element.innerText = word.substring(0, i);
yield
}
}
resolve()
}catch(e){
reject(e)
}finally{
clearTimeout(autoTyping)
}
})()
const autoTyping = setInterval(() => iterator.next(), interval);
iterator.next()
})
promise.stop = function stop(){
stopped = true
}
return promise
}
const word = "I love JS More than any Programming Language in the world!";
const h3 = document.getElementById("myh3");
const promise1 = typewriter(h3, word, 100)
promise1.then(() => console.log('1st example done'))
setTimeout(promise1.stop, 10000)
typewriter(null, word, 100) //Cause error with null element
.catch(e => console.error('2nd example failed:', e.stack))
<h3 id="myh3"></h3>

Progressively add strings from array to html element using timeout

I'm trying to add a string to a html element every 500ms using a for loop to pass the string to a function which updates the target element.
I'm not sure if i'm approaching this the right way or if it's possible as it just displays the strings all at once rather than every 500ms.
The desired effect is the strings displays as if someone is typing.
The code is below and here is a jsFiddle.
var content = "Hello, Universe!";
var split = content.split("");
var target = document.getElementsByClassName('place-here');
for (i = 0; i < split.length; i++) {
addChar(split[i]);
}
function addChar(char) {
if (timer) {
clearTimeout(timer);
}
var timer = setTimeout(function() {
target[0].innerHTML += char;
}, 500);
}
Just a proposal without setTimeout, but with setInterval and some other changes.
var content = "Hello, Universe!",
target = document.getElementById('ticker'),
i = 0,
timer = setInterval(addChar, 500);
function addChar() {
if (i < content.length) {
target.innerHTML += content[i];
i++;
} else {
clearTimeout(timer);
}
}
<div id="ticker"></div>
The problem is that all timeout functions starts at the same time (in 500ms). You can compute different timeouts by multiplying interval with the index of char in array:
for (i = 0; i < split.length; i++) {
addChar(split[i], i);
}
function addChar(char, i) {
setTimeout(function () {
target[0].innerHTML += char;
}, 500 * i);
}
You can mix setInterval and shift to do this :
var target = document.getElementsByClassName('place-here');
function display(element, string, timer) {
var split = string.split("");
var interval = setInterval(function() {
element.innerHTML += split.shift()
if (split.length == 0) {
clearInterval(interval);
}
}, timer)
}
display(target[0], "Hello, Universe!", 500)
I made you a working javascript code that you can paste in your fiddle. Your problem was that you call the setTimetout all at the same time so after 500 milliseconds everything executes instead of waiting on the other one to finish
var content = "Hello, Universe!";
var split = content.split("");
var target = document.getElementsByClassName('place-here');
console.log(split);
addChar(0);
function addChar(i) {
var currentChar = split[i];
console.log(i);
console.log(currentChar);
setTimeout(function() {
if(typeof currentChar !== 'undefined'){
target[0].innerHTML += currentChar;
addChar(i+1);
}
}, 1000);
}
Why not using setInterval() :
here is a fiddle
code here :
var content = "Hello, Universe!";
var split = content.split("");
var target = document.getElementsByClassName('place-here');
var length = split.length;
var count = 0;
var timer = setInterval(function() {
addChar(split[count]);
count++;
if(count>=length) clearInterval(timer);
}, 500);
function addChar(char) {
target[0].innerHTML += char;
}
<div class="place-here">
</div>
I made a few changes in your jsfiddle: https://jsfiddle.net/vochxa3f/10/
var content = "Hello, Universe!";
var split = content.split("");
var target = document.getElementsByClassName('place-here');
function addChar(str, i) {
if(i < str.length) {
target[0].innerHTML += str[i];
setTimeout(addChar, 500, str, ++i);
}
}
setTimeout(addChar(content, 0), 500);
The addChar function call itself with setTimeout() incrementing the variable i so in the next time it'll get another character from content.
Note that setTimeout() first argument is the reference to the function, in this case only "addChar" without "()". The arguments for the function stars at 3rd parameter and forth.

How to automatically type text repeatedly

I am trying to make a javascript/jquery code in which it automatically types a string, and then after the string is completely typed, clears the previously typed string and types it again. Currently, all my code does is type the string once. I know how to keep looping it, but it just starts typing where it left off, and doesn't clear the string. Javascript:
var txt='Typing text...'.split('');
var delay=850;
for ( i=0; i<txt.length;i++){
setTimeout(function(){
$('.autoText').append(txt.shift() )
}, delay * i)
}
and here is my html:
<h1 class="autoText" style="font-size: 50px; color: #7CFC00; margin-top: 0em; margin-left: 25%;"></h1>
var txt = 'Typing text...'.split(''),
$h1 = $('.autoText'),
len = txt.length,
delay = 850,
i = 0;
setInterval(function() {
$h1.append(txt[i++]);
if (i > len) {
$h1.empty();
i = 0;
}
}, delay);
DEMO: http://jsfiddle.net/yt6hm4hc/
How about this:
var txt='Typing text...';
var delay=850;
var i = 0;
function type() {
if (i < txt.length) {
$('.autoText').append(txt[i]);
i++;
} else {
$('.autoText').text('');
i = 0;
}
setTimeout(type, delay);
}
type();
http://jsfiddle.net/16v15ufv/
Forgive the overkill, but if you make everything really generic then you can re-use bits in other places
A function to generate each char of a string, one per invocation
function stringGenerator(str) {
var i = 0;
return function () {
if (i < str.length)
return str.charAt(i++);
i = 0;
return null;
};
}
A function to loop using setTimeout, with a couple neat little tricks for ending the loop
function timeoutLoop(fn, freq, callback) {
function looper() {
var ret = fn();
if (ret !== false && ret !== null && ret !== undefined)
return window.setTimeout(looper, freq);
if (callback)
callback();
}
window.setTimeout(looper, freq)
}
A function which combines these with logic about text in the DOM, to produce your typing effect
function type(node, str, freq) {
var s = stringGenerator(str),
f = function () {
var chr = s();
if (chr === null)
return false;
if (node.lastChild.nodeType === 3)
node.lastChild.data += chr;
else
node.appendChild(document.createTextNode(chr));
return true;
};
timeoutLoop(f, freq);
}
Finally, invocation, e.g. to have the words Hello world! written to the <body> one character every 500 ms
type(document.body, 'Hello world!', 500);
This is 100% vanilla
Is this what you want?
DEMO
var txt = 'Typing text...'.split('');
var delay = 850;
function type() {
for (i = 0; i < txt.length; i++) {
setTimeout(function () {
$('.autoText').append(txt.shift());
}, delay * i);
}
// delay * i + 1000 means 1 second after the above text was finished typing
setTimeout(function(){
// after finish, clear text in h1 element
$('.autoText').text('');
// fire `type` method again, with `txt` reset to its original value
type(txt = 'Typing text...'.split(''));
}, delay * i + 1000);
}
type(); // call type at least once
var txt='Typing text...'.split('');
var delay=850;
for ( i=0; i<txt.length;i++){
setTimeout(function(){
$('.autoText').text('');
$('.autoText').append(txt.shift() )
}, delay * i)
}
You could use recursion:
var txt='Typing text...';
var delay=850;
function TypeText($el, txt, currentIndex, timeout) {
setTimeout(function() {
//If finished writing, restart at 0
if (currentIndex >= txt.length) {
TypeText($el, txt, 0, 1000);
return;
}
//If at 0, empty the text
if (currentIndex == 0) $el.empty();
//Append current letter
$el.append(txt[currentIndex]);
//Append next letter
TypeText($el, txt, currentIndex+1, timeout);
}, timeout);
}
TypeText($('.autoText'), txt, 0, delay)
fiddle here
Using setTimeout within a loop you'll create your own pain in the neck.
You could use recursion, something like this:
function repeatEternal(str, time){
time = time*1000 || 200;
var position = 0
,rdiv = document.querySelector('#repeater')
,l2r = true;
return eternal(str);
function eternal(s){
rdiv.textContent = l2r ? rdiv.textContent + s[0] : s.slice(0, -1);
l2r = s.length === 1 ? !l2r : l2r;
s = s.length < 2 ? str : !l2r ? s.slice(0,-1) : s.slice(1);
setTimeout( function(){ eternal(s); }, time);
}
}
// usage
repeatEternal('Typing galore!', 0.3);
Here's the jsFiddle
Here is the OOP solution,with Object literal way. also with method that you can change the Delay. and in the future you can add more methods like change case of string or change effects of animation with setters methods. you can append it to any element with any string.
also it's not have dependencies like a jQuery. and this code you must put in the bottom of the DOM if you don't want dependency of jQuery or inside document ready func and with that you have dependency .
Hope it's help you.
var animText = {
animStr: '',
delay: 300,
setDelay: function(delay) {
this.delay = delay;
},
DoAnimation: function(string, selector) {
this.animStr = string.split('');
for (var i = 0; i <= string.length-1; i++) {
setTimeout(function () {
document.getElementById(selector).innerHTML += animText.animStr.shift();
},this.delay * i);
};
setTimeout( function() {
document.getElementById(selector).innerHTML = '';
animText.DoAnimation(string, selector);
}, this.delay * i + 1200)
}
}
animText.DoAnimation("Hello", "animStr");
animText.setDelay(900);

setTimeout in a loop and pass variables to use later

Consider the following code:
function func() {
var totalWidths = 0;
for( var i = 0, count = arr.length; i < count; i++ ) {
var image = arr[i];
insertElemInDOM(image);
preloadImage(image,function(){
var w = image.width();
totalWidths += w;
});
}
// do something with the variable "totalWidths"
doSomething(totalWidths)
}
I have 2 problems here. The image will be always the same (first problem), which one can solve with an anonymous function:
for(...) {
(function(image) {
preload(image,function() {
// now image is the correct one
});
})(image);
}
But how do I manage the totalWidths variable in order to use it later on doSomething(totalWidths)? The previous code would have a value of 0 for totalWidths.
Thanks!
You could timeout the whole loop and the doSomething, that's much more performant than setting up so many timeouts:
setTimeout(function() {
var inc = 0;
for (var i = 0; i < count; i++) {
var w = arr[i].width();
inc++;
}
doSomething(inc);
}, 1000);
However, what you actually seem to want are nested timeouts, i.e. waiting 1s for each iteration step and doing something after all have finished:
var inc = 0, count;
function asyncLoop(i, callback) {
if (i < count) {
var w = arr[i].width();
inc++;
setTimeout(function() {
asyncLoop(i+1, callback);
}, 1000);
} else {
callback();
}
}
asyncLoop(0, function() {
doSomething(inc);
});
OK, now that we know what you need the solution is to check after each load event whether all images are loaded:
var totalWidths = 0,
count = arr.length,
loaded = 0;
for (var i = 0; i < count; i++)
(function (image) {
insertElemInDOM(image);
preload(image, function() {
totalWidths += image.width();
// counter:
loaded++;
if (loaded == count-1) // the expected value
doSomething(totalWidths); // call back
});
})(arr[i]);

Categories