Wait for Javascript function finished inside foreach cshtml? - javascript

I'm trying to make simple autotype with Javascript. My problem is the autotype is cannot run sequentially if i call it from view (.cshtml).
My cshtml like this:
<script type="text/javascript" src="#Url.Content("/Scripts/autotype.js")"></script>
#foreach (var temp in #Model)
{
<script>
auto_type("ABCDEFG", 0)
</script>
}
<div id="divauto"></div>
and it's autotype.js :
function auto_type(wrt, i) {
wrt = wrt.split('');
var delay = 100;
while (i < wrt.length) {
setTimeout(function () {
$('#divauto').append(wrt.shift())
}, delay * i)
i++;
}
}
From these codes, the output will be like "AAABBBCCCDDDEEEFFFGGG" but i need the output like: "ABCDEFGABCDEFGABCDEFG"

You can do this with jQuery deferreds. In this case I had to use two recursive functions since you need flow control for both the iteration through the viewItems list as well as for kicking off setInterval() to update #divAuto for each character.
As an example of how promises are used here to add control, in autoType() you get a promise returned from the new autoTypeUtil(). When the promise is resolved within autoTypeUtil() you call autoType() again with the remaining list. This gives you flow control for the input items while still running asynchronously.
You may check out the fiddle for a working example.
function timeString(arr, timeFactor, deferred) {
if (arr.length === 0) { deferred.resolve(); }
var ch = arr.shift();
var deferredInternal = new $.Deferred();
var promise = deferredInternal.promise();
var delay = 100;
setTimeout(function () {
$('#divauto').append(ch);
deferredInternal.resolve("done");
}, delay * timeFactor)
promise.done(function() {
timeString(arr, timeFactor, deferred);
});
}
function autoTypeUtil(inputString, timeFactor) {
var deferredTimeString = new $.Deferred();
var deferredInternal = new $.Deferred();
var promiseTimeString = deferredTimeString.promise();
inputString = inputString.split('');
timeString(inputString, timeFactor, deferredTimeString);
promiseTimeString.then(function() {
deferredInternal.resolve()
});
return deferredInternal.promise();
}
function autoType(arr, timeFactor) {
if (arr.length === 0) { return; }
var promise = autoTypeUtil(arr.shift(), timeFactor);
promise.done(function() {
autoType(arr, timeFactor);
});
}
var viewItems = ["I", "love", "jQuery", "promises"];
autoType(viewItems, 1);

I think what you are looking for is a typewriter, or teletype effect, where the text is written out like a typewriter?
In that case, check out this SO question, as it looks like it will provide what you need.
Typewriter Effect with jQuery

The issue is that i is getting set to 7 at the end of your loop, which occurs before the first instance of it being called. Try something like this:
function auto_type(wrt) {
wrt = wrt.split('');
if (wrt.length > 0) {
var delay = 100;
type_loop(wrt, delay);
}
}
function type_loop(wrt, delay) {
var myTimeout = setTimeout(function () {
clearTimeout(myTimeout);
$('#divauto').text($('#divauto').text() + wrt.shift());
if (wrt.length > 0) {
type_loop(wrt, delay);
}
}, delay);
}
$(document).ready(function() {
auto_type("ABCDEFG", 0);
});
This method is called tail recursion in case you're curious.

Related

Is there a better way to do callback chaining in javascript?

I wrote a callback helper, that lets me group multiple callbacks into one function variable:
function chainCallbacks() {
var callbacks = arguments;
return function () {
for(var i = 0; i < callbacks.length; i++) {
if(callbacks[i] != null) {
callbacks[i].apply(null, arguments);
}
}
};
}
this works, but I'm wondering if there are any javascript libraries that provide the same functionality? or even better, something that simulates the .NET "event" pattern?
myEvent+=myCallback;
I have modified your chainCallbacks function. You can test below code in JS console (I'm using Chrome -works fine), and check the result.
var result = 0;
function a() {
result += 5;
console.log(result);
_next();
}
function b() {
result += 10;
console.log(result);
_next();
}
function c() {
result += 20;
console.log(result);
_next();
}
function chainCallbacks() {
var _this = this;
var _counter = 0;
var _callbacks = arguments;
var _next = function() {
_counter++;
if(_counter < _callbacks.length) {
_callbacks[_counter].apply(_this);
}
};
_this._next = _next;
return function() {
if(_callbacks.length > 0) {
_callbacks[0].apply(_this);
}
};
}
var queue = chainCallbacks(a, b, c);
queue();
Idea is simple - you call _next() whenever your callback function has finished executing, and you want to jump to another. So you can call _next() e.g. after some jQuery animation as well, and this way you will preserve the order of the functions.
If you want to replace a callback with one that calls the original as well as some others, I'd probably just do something like this:
Requirejs.config.callback = function(orig) {
var fns = [orig, first, second, third];
return function() {
fns.forEach(function(fn) { fn.apply(null, this); }, arguments);
};
}(Requirejs.config.callback);
But if you're doing this often, I think your solution will be as good as it gets. I don't see need for a library.
Requirejs.config.callback = chainCallbacks(Requirejs.config.callback, first, second, third)
A library can't do anything to extend language syntax in JavaScript. It's limited to what's available... no operator overloading or anything.

calling three subsequent callback functions

I'm trying to apply what I learned about callback functions in this post I made to extend to 3 functions, but am having some trouble getting things working. Can someone please help me understand how I can get these three functions to fire in sequence?
var yourCallback = function(args, second) {
var t = setTimeout(function() {
$('body').append(args);
}, 800);
second('3-');
}
var yourSecondCallback = function(args) {
var t = setTimeout(function() {
$('body').append(args);
}, 800);
}
function function1(args, callback, yourSecondCallback) {
$('body').append(args);
if (callback) {
callback('2-');
}
}
function1('1-' , yourCallback);​
http://jsfiddle.net/loren_hibbard/WfKx2/3/
Thank you very much!
You need to nest the callbacks to get them to call in order.
var yourCallback = function(args, second) {
var t = setTimeout(function() {
$('body').append(args);
second('3-');
}, 800);
}
var yourSecondCallback = function(args) {
var t = setTimeout(function() {
$('body').append(args);
}, 800);
}
function function1(args, callback) {
$('body').append(args);
if (callback) {
callback('2-', yourSecondCallback);
}
}
function1('1-' , yourCallback);​
Here's your altered fiddle
Your function names confuse me, so I'm just going to make some up to demonstrate a simple approach:
function1('-1', function(){
secondCallback(...);
thirdCallback(...);
...
});
Any reason a simple approach like that won't work for you?
Not sure exactly what you are trying to do here, but when you do this in function1:
callback('2-');
You are calling this method:
var yourCallback = function(args, second)
But you are not providing a value for second, so you get an error.
If your first argument and only is going to be an input for all the callbacks then this code can be used for unlimited arguments
var yourCallback = function(args, second) {
var t = setTimeout(function() {
$('body').append(args + ' first function');
}, 800);
}
var yourSecondCallback = function(args) {
var t = setTimeout(function() {
$('body').append(args + ' second function');
}, 800);
}
function function1(args) {
var callbacks = arguments.length - 1;
for (i = 1; i < arguments.length; i++) {
if (typeof(arguments[i] == 'function')) {
arguments[i](arguments[0]);
}
}
}
function1('1-', yourCallback, yourSecondCallback);​
Fiddle - http://jsfiddle.net/8squF/
I have a function, mainMethod, that is calling three callback functions.
mainFunction the first callback function(one) will be called and the first parameter will be passed into it.
one will pass the second parameter into the second callback function (two).
two will pass the third parameter into the last callback function (three).
three will just log the last parameter that was passed into it.
function mainFunction(callback1, callback2, callback3){
var first_parameter = "ONE"
callback1(first_parameter);
}
function one(a){
console.log("one: " + a);
var second_parameter = "TWO"
two(second_parameter);
}
function two(b){
console.log("two: " + b);
var third_parameter = "THREE";
three(third_parameter);
}
function three(c){
console.log("three: " + c);
}
mainFunction(one, two, three);

Something similar to jQuery when / then: deferred execution with break

I'm searching for a solution where I'm able to run different functions, but some of them need a timeout and all following functions need to wait until the previous one is finished. Every function should be able to break the complete process.
Now I thought pushing all functions to a stack and loop through them:
function foo() {
// bar() should wait as long the following is finished:
setTimeout(function(){
if ((new Date()).getSeconds() % 2) {
alert('foo');
// break loop through functions (bar is not called)
}
else {
// start next function (bar is called)
}
}, 1000);
}
function bar() {
setTimeout(function(){
alert('bar')
}, 1000);
}
var functions = new Array('foo', 'bar');
for (var i = 0, length = functions.length; i < length; i++) {
window[functions[i]]();
}
But how to include wait/break?!
Note: This should work with 2+ functions (amount of functions is changeable)
Note2: I don't want to use jQuery.
Note: I have updated my answer, see bottom of post.
Alright, let's take a look.
You're using the window[func]() method, so you should be able to store and use return values from each function.
Proof:
function a(){
return "value";
}
var ret_val = window['a']();
alert(ret_val);
Let's create a return rule:
If function returns true, continue execution flow.
If function returns false, break execution flow.
function a(){
//Do stuff
return (condition);
}
for(i = 0; i < n; i++){
var bReturn = window['function']();
if(!bReturn) break;
}
Now let's put it into practice.
function a(){
//Do stuff
return ((new Date()).getSeconds() % 2); //Continue?
}
function b(){
//Do stuff
return true; //Continue?
}
function c(){
//Do stuff
return false; //Continue?
}
function d(){
//Do stuff
return true; //Continue?
}
var functions = new Array('a', 'b', 'c', 'd');
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) break;
}
Depending on when you execute the script, eg, an even or uneven time period, it will only execute function a or execute functions a b & c. In between each function, you can go about your normal business.
Of course, the conditions probably vary from each individual function in your case.
Here's a JSFiddle example where you can see it in action.
With some small modification, you can for instance, make it so that if function a returns false, it will skip the following function and continue on to the next, or the one after that.
Changing
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) break;
}
To this
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) i++;
}
Will make it skip one function, every time a function returns false.
You can try it out here.
On a side-note, if you were looking for a waiting function that "pauses" the script, you could use this piece of code.
function pausecomp(millis){
var date = new Date();
var curDate = null;
do {
curDate = new Date();
}while(curDate-date < millis);
}
Update
After adjusting the code, it now works with setTimeout.
The idea is that you have an entry point, starting with the first function in the array, and pass along an index parameter of where you currently are in the array and then increment index with one to execute the next function.
Example | Code
function next_function(index){
if(index >= functions.length) return false;
setTimeout(function(){
window[functions[index+1]](index+1);
}, 1000);
}
function a(index){
//Do stuff
if(((new Date()).getSeconds() % 2)) return false; //Stop?
next_function(index);
}
function b(index){
//Do stuff
if(false) return false; //Stop?
next_function(index);
}
function c(index){
//Do stuff
if(true) return false; //Stop?
next_function(index);
}
function d(index){
//Do stuff
if(false) return false; //Stop?
next_function(index);
}
var functions = new Array('a', 'b', 'c', 'd');
//entry point
window[functions[0]](0);
This is exactly the scenario promises solve. In particular, the fact that promises can be broken is perfect for your situation, since a broken promise prevents the chain from continuing (just like a thrown exception in synchronous code).
Example, using the Q promise library discussed in the above-linked slides:
function fooAsync() {
return Q.delay(1000).then(function () {
if ((new Date()).getSeconds() % 2) {
alert("foo");
throw new Error("Can't go further!");
}
});
}
function barAsync() {
return Q.delay(1000).then(function () {
alert("bar");
});
}
var functions = [fooAsync, barAsync];
// This code can be more elegant using Array.prototype.reduce, but whatever.
var promiseForAll = Q.resolve();
for (var i = 0; i < functions.length; ++i) {
promiseForAll = promiseForAll.then(functions[i]);
}
// Of course in this case it simplifies to just
// promiseForAll = fooAsync().then(barAsync);
promiseForAll.then(
function () {
alert("done!");
},
function (error) {
alert("someone quit early!");
// and if you care to figure out what they said, inspect error.
}
).end();

How to call the javascript function dynamically

I need to call the javascript function dynamically after some delay, The function display_1, 2, ... n will be dynamically constructed. My script looks like this, but the function never gets triggered if I use the following code, but if I hardcode the function it just seems to be fine.
function display_1() {
alert(1);
}
function display_2() {
alert(2);
}
function display() {
var prefix = 'display_';
for(var i = 1; i < 3; i++) {
setTimeout(prefix.concat(i), 1000);
}
window.onload = display();
Instead of going via a string, you may as well group the functions into an array:
function display_1() {...}
function display_2() { ... }
var functions = [ display_1, display_2 ];
function display() {
for( var i = 0; i != functions.length; ++i ) {
setTimeout( functions[i], 1000 );
}
}
If you want to go further, you may even leave out the explicit function names:
var functions = [
function() { /*the function_1 implementation*/
},
function() { /*the function_2 implementation*/
}
];
you have to add the parenthesis so that the function is called:
setTimeout(prefix.concat(i)+"()", 1000);
or simply:
setTimeout(prefix + i + "()", 1000);
Besides of that please note that both functions are called pratically at the same time, because the timers started with ´setTimeout()` start at the same time.
Depending on what you're trying to do you might have a look at setInterval() or start the second timeout at the end of the display_1() function.
It should be
function display_1() {
alert(1);
}
function display_2() {
alert(2);
}
function display() {
var prefix = 'display_';
for(var i = 1; i < 3; i++) {
setTimeout(prefix.concat(i)+'()', 1000);
}
}
window.onload = display;
the string passed to setTimeout should call the function
onload should be set to a function, not its return value
setInterval('load_testimonial()',5000);//first parameter is your function or what ever the code u want to execute, and second is time in millisecond..
this will help you to execute your function for every given time.
If you really want a 1000ms delay between executing the functions, you could do something like this:
window.onload = function() {
var n = 0;
var functions = [
function() {
alert(1);
setTimeout(functions[n++], 1000);
},
function() {
alert(2);
setTimeout(functions[n++], 1000);
},
function() {
alert(3);
}
];
setTimeout(functions[n++], 1000);
};
(rewrite it in a less-repetitive nature if needed)

calling setinterval with object reference

I have a banner rotator and I wanted to use objects instead of functions so I could make the code more efficient. Anyway I can't seem to get setInterval to work. I think it's got something to do with the object reference. Can anybody explain this? Here's what I've got so far:
window.addEvent('domready', function() {
function set_banner(divid, array)
{
var banner = $(divid);
banner.set('html', '<img src="" alt=""/>');
var banner_link = $(divid).getElement('a');
var banner_image = $(divid).getElement('img');
var delay = 0;
for (var keys in banner1array) {
var callback = (function(key) { return function() {
banner.setStyle('opacity', 0);
var object = array[key];
for (var property in object) {
if (property == 'href') {
var href = object[property];
}
if (property == 'src') {
var src = object[property];
}
}
if (!banner.getStyle('opacity')) {
banner.set('tween', {duration:1000});
banner_link.setProperty('href', href);
banner_image.setProperty('src', src);
banner.tween('opacity', 1);
}
}; })(keys);
setTimeout(callback, delay);
delay += 21000;
}
}
var banner1 = set_banner('banner1', banner1array);
setInterval(function() {set_banner('banner1', banner1array);}, 84000);
var banner2 = set_banner('banner2', banner2array);
setInterval(function() {set_banner('banner2', banner2array);}, 84000);
});
A couple of simple mistake:
var banner1 = new set_banner('banner1');
^ ---------- creates a new object and uses set_banner as the constructor
your code already gets called here
and you get a new object back, which in this case has NO use
....
setInterval(banner1(), 42000);
^----------------- The parenthesis EXECUTE the function
the RETURN VALUE is then passed to setInterval
BUT... banner1() is NOT a function, so this fails
What you want to do in case that you want to call set_banner after 42 seconds AND pass a parameter is to use an anonymous function which then calls set_banner.
setInterval(function() { // pass an anonymous function, this gets executed after 42 seconds...
set_banner('banner1'); // ...and then calls set_banner from within itself
}, 42000);
Something else to consider: http://zetafleet.com/blog/why-i-consider-setinterval-harmful.
(tl:dr Instead of setInterval, use setTimeout.) While I'm not sure that his arguments apply here, it seems like a good thing to get in the habit of avoiding.
function defer_banner(div, bannerArray, delay) {
setTimeout(function() {
setBanner(div, bannerArray);
defer_banner(div, bannerArray, delay);
}, delay);
});

Categories