I want to call a second instance of the same function but with different values, after the first instance has completely finished, currently it calls both instances at the same time.
function printLetterByLetter(destination, message, speed) {
var i = 0;
var interval = setInterval(function () {
document.getElementById(destination).innerHTML += message.charAt(i);
i++;
if (i > message.length) {
clearInterval(interval);
}
}, speed);
}
printLetterByLetter("hc-a", "Hello world", 100);
printLetterByLetter("hc-b", "Hello world again.", 100);
How can I do this?
You can do using promise which wait for your first function execution then execute next otherwise you can use async/await which is also a good alternative.
Using Promise
function printLetterByLetter(destination, message, speed) {
var i = 0;
return new Promise(function(resolve, reject) {
var interval = setInterval(function() {
document.getElementById(destination).innerHTML += message.charAt(i);
i++;
if (i > message.length) {
clearInterval(interval);
resolve(true);
}
}, speed);
});
}
printLetterByLetter("hc-a", "Hello world", 100).then(function(resolve) {
printLetterByLetter("hc-b", "Hello world again.", 100);
}, function(reject) {});
Using async/await
function printLetterByLetter(destination, message, speed) {
var i = 0;
return new Promise(function(resolve, reject) {
var interval = setInterval(function() {
document.getElementById(destination).innerHTML += message.charAt(i);
i++;
if (i > message.length) {
clearInterval(interval);
resolve(true);
}
}, speed);
});
}
(async function() {
await printLetterByLetter("hc-a", "Hello world", 100);
printLetterByLetter("hc-b", "Hello world again.", 100);
})()
You can use Promises or async/await in order to do this. See the example below, that achieves your goal by utilizing Promises:
function printLetterByLetter(destination, message, speed) {
var i = 0;
// Return promise instance which you can use to execute code after promise resolves
return new Promise(function(resolve) {
var interval = setInterval(function() {
document.getElementById(destination).innerHTML += message.charAt(i);
i++;
if (i > message.length) {
clearInterval(interval);
// Resolve promise and execute the code in "then" block
resolve();
}
}, speed);
});
}
printLetterByLetter('hc-a', 'Hello world', 100).then(function() {
// This code gets executed when promise resolves
printLetterByLetter('hc-b', 'Hello world again.', 100);
});
<div id="hc-a"></div>
<div id="hc-b"></div>
You could use a classical approach with a stack and test the stack if the actual interval has ended.
var printLetterByLetter = function () {
function startInterval() {
var data = stack.shift(),
i = 0;
return data && setInterval(function () {
document.getElementById(data.destination).innerHTML += data.message[i++];
if (i >= data.message.length) {
clearInterval(interval);
interval = startInterval();
}
}, data.speed);
}
var stack = [],
interval;
return function (destination, message, speed) {
stack.push({ destination, message, speed });
interval = interval || startInterval();
};
}();
printLetterByLetter("hc-a", "Hello world", 100);
printLetterByLetter("hc-b", "Hello world again.", 50);
printLetterByLetter("hc-c", "See you later, alligator!", 200);
<div id="hc-a"></div>
<div id="hc-b"></div>
<div id="hc-c"></div>
Related
I am new to Javascript and currently working on a website, that changes how it looks by itself over time.
One part of this page is a "Typewriter", that writes out a text letter by letter.
This is the code for this Typewriter:
function typeWriter(element, txt) {
if (txt.length > 1) {
element.textContent += txt.charAt(0);
var newText = txt.slice(1,txt.length);
setTimeout(typeWriter, 150 , element, newText);
} else {
element.textContent += txt.charAt(0);
}
}
Now I want to wait for the typewriter-function to finish with its text before doing another change to let's say my background-color.
function runThis(){
var line1 = document.getElementById("line1");
typeWriter(line1, "This should be written first, before continuing");
document.body.style.backgroundColor = "blue";
}
To my understanding, the setTimeout makes my typewriter async, so if I do it as in the example above the third line of code will run as soon as the typewriter hits the first setTimeout.
I tried to realize this with the async/await terms and promises. But even after I resolve the promise, my "runThis" function won't continue after the typewriter finishes.
function typeWriter(element, txt) {
return new Promise (function(resolve,reject) {
if (txt.length > 1) {
element.textContent += txt.charAt(0);
var newText = txt.slice(1,txt.length);
setTimeout(typeWriter, 150, element, newText);
} else {
element.textContent += txt.charAt(0);
resolve();
}
})
}
async function runThis() {
var line1 = document.getElementById("line1");
await typeWriter(line1, "papupa");
console.log("waiting over")
document.body.style.backgroundColor = "blue";
}
Can you please help me figure out what's wrong here?
Thank you very much
You can wrap the setTimeout in a promise. This will allow you to use the async/await syntax to express the intention of your code more clearly, almost as if it was running synchronously.
async function runThis() {
var line1 = document.getElementById("line1");
await typeWriter(line1, "papupa");
document.body.style.backgroundColor = "blue";
}
async function typeWriter(element, txt) {
for (var i = 0; i < txt.length; i++) {
element.textContent += txt[i]; // this is shorthand for txt.charAt(i)
await pause();
}
}
function pause() {
return new Promise(function(resolve, reject) {
setTimeout(resolve, 150);
});
}
You can pass the function to run at the end as another parameter.
function typeWriter(element, txt, callback) {
if (txt.length > 1) {
element.textContent += txt.charAt(0);
var newText = txt.slice(1,txt.length);
setTimeout(typeWriter, 150 , element, newText, cb);
} else {
element.textContent += txt.charAt(0);
callback();
}
}
typeWriter(el, "ABCDEF", runthis);
You're creating more than one promises and only resolving the last one.
You could define the recursive function as an inner function, here is an example:
function typeWriter(element, text) {
return new Promise (function(resolve,reject) {
function recursion(txt) {
element.textContent += txt.charAt(0);
if (txt.length > 1) {
var newText = txt.slice(1, txt.length);
setTimeout(recursion, 150, newText);
} else {
resolve();
}
}
recursion(text);
});
}
I am using trying to have typing text in a div that has three different lines of text. So I want to call the function typed_text() for line one, then call it again for line two, then line three. I tried to use promises, but I was lost when it came to using setTimeout() and recursion.
I call the function something like this.
typed_text("#line_one", frame.text_one, 0, 25)
typed_text("#line_two", frame.text_two, 0, 25)
typed_text("#line_three", frame.text_three, 0, 25)
function typed_text(div, text, index, interval)
{
if (quit_typed_text == true) {
interval = 0;
}
if (index < text.length) {
$(div).append(text[index++]);
setTimeout(function() {typed_text(div, text, index, interval); }, interval);
} else {
click_disabled = false;
}
}
Edit: Here is what I was attempting using recursion and promises
typed_text("#text_one", frame.text_one, 0, 25).then(typed_text("#text_two", frame.text_two, 0, 25));
function typed_text(div, text, index, interval)
{
return new Promise(function (resolve) {
if (quit_typed_text == true) {
interval = 0;
}
if (index >= text.length) {
click_disabled = false;
resolve();
}
}).then(function() {
if (index < text.length) {
$(div).append(text[index++]);
/* Problem is here, setTimeout.then() does not exist as a function */
setTimeout(function() {typed_text(div, text, index, interval); }, interval);
}
});
}
using promises, your typed_text function can be rewritten as follows
function typed_text(div, text, index, interval) {
return new Promise(function(resolve) {
var doit = function() {
if (quit_typed_text == true) {
interval = 0;
}
if (index < text.length) {
$(div).append(text[index++]);
setTimeout(doit, interval);
} else {
click_disabled = false; // not sure this should be here
resolve();
}
};
doit(); // oops forgot this line
});
}
Then, to use it, simply use promise chaining as follows:
typed_text("#line_one", frame.text_one, 0, 25).then(function() {
return typed_text("#line_two", frame.text_two, 0, 25);
}).then(function() {
return typed_text("#line_three", frame.text_three, 0, 25);
}).then(function() {
// all done here - perhaps this is where click_disabled = false should be?
});
It seems there's no asynchronous function in your code, so why don't you call your function "typed_text" in a setTimeOut ?
By example :
setTimeout(function() { typed_text("#line_one", frame.text_one, 0); }, 25);
setTimeout(function() { typed_text("#line_two", frame.text_two, 0); }, 50);
setTimeout(function() { typed_text("#line_three", frame.text_three, 0); }, 75);
function typed_text(div, text, index){
if (quit_typed_text == true) interval = 0;
if (index < text.length) $(div).append(text[index++]);
else click_disabled = false;
}
Could this be ok for you ?
Here is something wrong. All functions should be called synchronously. Could anyone give me a hint? I think that is an error in the for loop
Here is my code:
var radioValues = msg.options;
var cn = gotoReport()
.then(clickReport)
.then(clickReportFake)
.then(clickNext);
for (var i = 0; i < radioValues.length; i++){
cn = cn.then(clickOption(radioValues[i])).then(clickNext);
}
cn.then(clickSendToFacebook).then(clickFinish);
//all called functions look like that
function clickNext(){
return process(function(){
console.log("clickNext");
var next = $('button:contains("Weiter")');
$(next).click();
},3000);
}
function process(action, ms) {
var deferred = $.Deferred();
timer = setInterval(function() {
deferred.notify();
}, 1000);
setTimeout(function() {
clearInterval(timer);
action();
deferred.resolve();
}, ms);
// return deferred;
return deferred.promise();
}
function sleep(ms)
{
return(new Promise(function(resolve, reject) {
setTimeout(function() { resolve(); }, ms);
}));
}
Here is the output
gotoReport
clickOption=option3
clickReport
clickReportFake
clickNext
clickNext
clickSendToFacebook
clickFinish
One major issue is here:
cn.then(clickOption(radioValues[i]))
You are not passing the clickOption function as argument to then -- you are invoking it. Instead do:
cn.then(clickOption.bind(null, radioValues[i]))
Say I have an array of functions that invoke a setTimeout.
[
function(cb){
setTimeout(function(){
cb('one');
}, 200);
},
function(cb){
setTimeout(function(){
cb('two');
}, 100);
}
]
Is there a way to access the time parameter (200, 100) and save the sum of that to a variable?
I want to execute a function only when both of those functions are done
A better approach is to use promises and Promise.all:
var task1 = new Promise(function(resolve,reject) {
setTimeout(function() {
//do something
resolve();
}, 100);
});
var task2 = new Promise(function(resolve,reject) {
setTimeout(function() {
//do something
resolve();
}, 200);
});
Promise.all([task1, task2]).then(function() {
//will be executed when both complete
});
You can mimic it with a closure for the count.
function out(s) {
var node = document.createElement('div');
node.innerHTML = s + '<br>';
document.getElementById('out').appendChild(node);
}
var f = [
function (cb) { setTimeout(function () { cb('one'); }, 100); },
function (cb) { setTimeout(function () { cb('two'); }, 200); }
],
useCounter = function () {
var count = 2;
return function (s) {
count--;
out(s + ' ' + count);
!count && out('done');
}
}();
f[0](useCounter);
f[1](useCounter);
<div id="out"></div>
I want to return a value inside a setInterval. I just want to execute something with time interval and here's what I've tried:
function git(limit) {
var i = 0;
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
return 'done';
}
i++;
}, 800);
}
var x = git(5);
console.log(x);
And it's not working.
Is there any other way?
What I'm going to do with this is to do an animation for specific time interval. Then when i reached the limit (ex. 5x blink by $().fadeOut().fadeIn()), I want to return a value.
This is the application:
function func_a(limit) {
var i = 0;
var defer = $.Deferred();
var x = setInterval(function () {
$('#output').append('A Running Function ' + i + '<br />');
if (i == limit) {
$('#output').append('A Done Function A:' + i + '<br /><br />');
clearInterval(x);
defer.resolve('B');
}
i++;
}, 500);
return defer;
}
function func_b(limit) {
var c = 0;
var defer = $.Deferred();
var y = setInterval(function () {
$('#output').append('B Running Function ' + c + '<br />');
if (c == limit) {
$('#output').append('B Done Function B:' + c + '<br /><br />');
clearInterval(y);
defer.resolve('A');
}
c++;
}, 500);
return defer;
}
func_a(3).then( func_b(5) ).then( func_a(2) );
This is not functioning well, it should print A,A,A,Done A,B,B,B,B,B,Done B,A,A,Done A but here it is scrambled and seems the defer runs all function not one after the other but simultaneously. That's why I asked this question because I want to return return defer; inside my if...
if (i == limit) {
$('#output').append('A Done Function A:' + i + '<br /><br />');
clearInterval(x);
defer.resolve('B');
// planning to put return here instead below but this is not working
return defer;
}
Do you expect it to wait until the interval ends? That would be a real pain for the runtime, you would block the whole page. Lots of thing in JS are asynchronous these days so you have to use callback, promise or something like that:
function git(limit, callback) {
var i = 0;
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
callback('done');
}
i++;
}, 800);
}
git(5, function (x) {
console.log(x);
});
Using a promise it would look like this:
function git(limit, callback) {
var i = 0;
return new Promise(function (resolve) {
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
resolve('done');
}
i++;
}, 800);
});
}
git(5)
.then(function (x) {
console.log(x);
return new Promise(function (resolve) {
setTimeout(function () { resolve("hello"); }, 1000);
});
})
.then(function (y) {
console.log(y); // "hello" after 1000 milliseconds
});
Edit: Added pseudo-example for promise creation
Edit 2: Using two promises
Edit 3: Fix promise.resolve
Try to get a callback to your git function.
function git(limit,callback) {
var i = 0;
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
callback('done') // now call the callback function with 'done'
}
i++;
}, 800);
}
var x = git(5,console.log); // you passed the function you want to execute in second paramenter