How to add dynamic promises or delay a current one? - javascript

My main goal is to delay a counter before it triggers.
Imagine a timeout couting down 'n' to '0' and then fire an event, but it can be reset asyncly to 'n' every time something happens.
I tried to achieve this using 'promises' with $q.
I was trying to add dynamicly new promises with timeout inside chaining a brand new delay.
But before achieve this goal, i couldn't even chain new promises.
Example:
var d1 = $q.defer();
var d2 = $q.defer();
d1.promise.then(function(data) {
return 'd1' + data;
});
d2.promise.then(function(data) {
return 'd2' + data;
});
var t = $q.all([d1.promise]);
t.then(function(data) {
console.log(data);
});
$timeout(function()
{
d1.resolve("1000");
}, 1000);
$timeout(function()
{
t.then(function() {
d2.resolve("800");
});
}, 800);
It only outputs: ["1000"] instead of ["1000", "800"]
My search so far:
stackoverflow did not helped me.
angular-promise-tracker I can't understand this code, how it behaves is exactly for what I need. But it is made for something else.

If you are willing to just use native javascript then this should work:
// create a handle to the timeout
var toHandle = window.setTimeout(X);
// put this in the event that causes a reset
window.clearTimeout(toHandle);
toHandle = window.setTimeout(X);
Where X is the function you want to occur on a delay. This also gives you the power to change the delay value as you wish.
Cheers.

Related

Array.pop() Isn't killing my setInterval

I've written some code that when you click a button it adds an instance of a function to an array,
var objects = [];
$(document).on("click", ".addButton", function(){
objects.push(new newObject(1));
});
function newObject(amount){
setInterval(function(){
addValue(amount);
}, 1000);
}
So then every second each new object created keeps running the addValue function every second adding the amount.
The problem is when I try and destroy that function with objects.pop() it deletes the object but the setInterval doesn't stop running.
How do I make it destroy everything in that function and stop it from running?
There is nothing quite like that in JS for setInterval. I would suggesting declaring a method to handle clean up.
// "Class" declaration
function newObject(amount) {
var id = setInterval(function() {
addValue(amount);
}, 1000);
this.kill = function() {
clearInterval(id);
}
}
// "Public" api for the data structure
var objects = [];
function addNewObject() {
objects.push(new newObject(1));
}
function destroyLastObject() {
objects.pop().kill();
}
// Event bindings
$(document).on("click", ".addButton", addNewObject);
$(document).on("click", ".removeButton", destroyLastObject);
Completely untested, but along these lines should work.
EDIT
This, imo, is a great resource for learning about different patterns within javascript - long but well well worth the read: https://addyosmani.com/resources/essentialjsdesignpatterns/book/
You got to find something to check against to clear the interval. I am clearing based on array length. It only executes once.
// you got to find something to check against to clear the interval
var objects = [];
document.addEventListener("click", function(){
console.log('click');
objects.push(new newObject(1));
});
function newObject(amount){
var interval= setInterval(function(){
if(objects.length !==0){
clearInterval(interval);
}
}, 1000);
}

Issue with using jQuery deferred

I am using jQuery Deferred. I want to do 2 things when I click on a link to open modal dialog;
Call my API
Call my template (where values from API would get populated)
So I am doing the following;
self.myModel = new myModel();
self.myModel.url = "api/myModel/";
self.scenarioRecInfoDeferred = new $.Deferred();
self.myModel.fetch({
success : function(){
myModelDeferred.resolve();
}
});
self.tmplDeferred = new $.Deferred();
$.when(self.myModelDeferred, self.tmplDeferred).done(function(modalTemplates) {
});
require(['text!templates/mytemp/my-tpl.html'], function(modalTemplates) {
self.tmplDeferred.resolve(modalTemplates);
});
Now I am not getting anything for "modalTemplates" inside
$.when(self.myModelDeferred, self.tmplDeferred).done(function(modalTemplates) {}
Am I doing something wrong in accessing/passing the parameter?
I believe you need 2 parameters in your .done() function handler. The first parameter will correspond to myModelDeferred arguments (which will be undefined) and the second will correspond to tmplDeferred arguments.
I made a quick jsfiddle to test myself.
var action1 = $.Deferred();
var action2 = $.Deferred();
setTimeout(function(){
action1.resolve();
}, 1000);
setTimeout(function(){
action2.resolve("def", "ghi");
}, 500);
$.when(action1.promise(), action2.promise()).done(function(args, args2){
console.log(args); // undefined
console.log(args2); // ["def", "ghi"]
});
DEMO
Hope this helps!
Why do you need to declare the deferred before loading the template?
You could do whatever you need to do with modalTemplates inside that require block and just skip the tmplDeferred use at all.
require(['text!templates/mytemp/my-tpl.html'], function(modalTemplates) {
self.myModelDeferred.done(function(modalTemplates) {
});
});
Anyway, if you want to stick to that structure, I don't think that's the way to pass a variable to a deferred. You could instead assign it as self.tmplDeferred.modalTemplates=modalTemplates. So, the next should work
$.when(self.myModelDeferred, self.tmplDeferred).done(function(self.tmplDeferred.modalTemplates) {
});
require(['text!templates/mytemp/my-tpl.html'], function(modalTemplates) {
self.tmplDeferred.modalTemplates=modalTemplates;
self.tmplDeferred.resolve();
});

Strange behavior with debounced/throttled function

I have a function that I need to call, every time a textbox value changes. However, it initializes an AJAX call, so I need to not run the script on every keystroke.
I wrote the following function as a proof of concept:
$(document).ready(function() {
var scheduledChange;
function triggerLinkChange(value, debounce) {
clearTimeout(scheduledChange);
scheduledChange = setTimeout(function(val) {
alert("value: "+val);
}(value), debounce);
}
$('input').keyup(function(){
triggerLinkChange($(this).val(),3000);
});
});
Unfortunately, the delay seems to not be working, properly. I'm not sure exactly what the issue is, as it appears to have a slight delay for the first call (maybe), but then fires with every keypress.
Any ideas?
You're calling the function that you declare in the timeout immediately, instead of passing it to the function.
However, it will actually work without binding or passing value to the function, because the variable is in scope anyway:
scheduledChange = setTimeout(function() {
alert("value: " + value);
}, debounce);
Here's an example: http://jsfiddle.net/RbLRu/
Seems too complicated ?
$('input').keyup(function(){
var self = this;
clearTimeout( $(this).data('timer') );
$(this).data('timer', setTimeout(function(){
// do ajax, 3 seconds seems like lot here
$.get('url', {value : self.value}, callback);
}, 3000));
});
A much simpler way is to have a variable like this (pseudo-code because I am just typing it out here):
var myTrigger;
$('input').keyup(function(){
// Cancel the countdown to do the ajax
window.clearTimeout(myTrigger);
// Now reset it to trigger in 3 seconds unless another key is pressed
myTrigger = window.setTimeout(function () { DoAjax(); }, 3000);
}
});

Worn out getting animation to sequence

This is originally from (Pause execution in while loop locks browser (updated with fiddles))
I have been at this all day and I can't figure out how to keep javascript from advancing to the next line and in essence executing all lines at once. I have tried every combination of delay / setTimeout I can think of to no avail.
I just want the elements in the array to flash once then pause, then do it again for another element in the array till all elements have been removed and the array is empty.
But because javascript is executing all lines at once I end up with the appearance of all elements flashing at the same time.
Here is the fiddle:
http://jsfiddle.net/ramjet/xgz52/7/
and the relevant code:
FlashElement: function () {
while (elementArray.length) {
alert('a ' + elementArray.length);
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
alert('delay complete');
elementArray = elementArray.not($el);
alert('array popped');
alert('z ' + elementArray.length);
}
},
ANSWER FOR THIS SITUATION. Hopefully it will help others.
As Zach Saucier points out the loop was really my problem...but not the only problem. I was the other problem(s).
Me first.
Fool that I am I was really causing my own complications with two things I was doing wrong.
First using jsfiddle my javascript would error due to syntax or some such thing but fiddle doesn't tell you that (to my knowledge) so my fiddle wouldn't run but I took it in pride as MY CODE IS FINE stupid javascript isn't working.
Second I was passing my function to setTimeout incorrectly. I was adding the function parens () and that is not correct either which would bring me back to issue one above.
WRONG: intervalTimer = setInterval(MyFunction(), 1500);
RIGHT: intervalTimer = setInterval(MyFunction, 1500);
As for the code. As Zach pointed out and I read here (http://javascript.info/tutorial/settimeout-setinterval) while he was responding setting a timeout in a loop is bad. The loop will iterate rapidly and with the timeout one of the steps in the loop we get into a circular firing squad.
Here is my implementation:
I created a couple variables but didn't want them polluting the global scope so I created them within the custom domain. One to hold the array of elements the other the handle to the setInterval object.
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
....
}
In my onReady function (the one the page calls to kick things off) I set my domain array variable and set the interval saving the handle for use later. Note that the interval timer is how long I want between images flashes.
onReady: function ()
{
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
},
Now instead of looping through the array I am executing a function at certain intervals and just tracking how many elements are left in the array to be flashed. Once there are zero elements in the array I kill the interval execution.
FlashElement: function ()
{
if(elementArray.length > 0) //check how many elements left to be flashed
{
var $el = PageLoadAnimation.GrabElement(); //get random element
PageLoadAnimation.FlashBlast($el); //flash it
PageLoadAnimation.RemoveElement($el); //remove that element
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
So the whole thing is:
var PageLoadAnimation =
{
elementArray: null,
intervalTimer: null,
onReady: function () {
elementArray = $('#PartialsContainer').children();
//black everything out just to be sure
PageLoadAnimation.BlackOutElements();
//flash & show
intervalTimer = setInterval(PageLoadAnimation.FlashElement, 1500);
//NOT this PageLoadAnimation.FlashElement()
},
BlackOutElements: function () {
$('#PartialsContainer').children().hide();
},
FlashElement: function ()
{
if(elementArray.length > 0)
{
var $el = PageLoadAnimation.GrabElement();
PageLoadAnimation.FlashBlast($el);
PageLoadAnimation.RemoveElement($el);
}
else
{
//done clear timer
clearInterval(intervalTimer);
intervalTimer = null;
}
},
GrabElement: function()
{
return elementArray.eq(Math.floor(Math.random() * elementArray.length));
},
RemoveElement: function($el)
{ elementArray = elementArray.not($el); },
FlashBlast: function ($el) {
//flash background
$el.fadeIn(100, function () { $el.fadeOut(100) });
}
}
Hope that help others understand the way to go about pausing execution in javascript.
The reason why you were having trouble is because setTimeout function is non-blocking and will return immediately. Therefore the loop will iterate very quickly, initiating each of the timeouts within milliseconds of each other instead of including the previous one's delay
As a result, you need to create a custom function that will wait on the setInterval to finish before running again
FlashElement: function () { // Call it where you had the function originally
myLoop();
},
...
function myLoop() {
setTimeout(function () { // call a setTimeout when the loop is called
var $el = elementArray.eq(Math.floor(Math.random() * elementArray.length));
PageLoadAnimation.FlashBlast($el);
elementArray = elementArray.not($el);
if (0 < elementArray.length) { // if the counter < length, call the loop function
myLoop();
}
}, 1000)
}
Feel free to change the delay to whatever value you wish (3000ms to let each fade finish before the last at the moment). If you want to start the fade in of the next before the previous ends and keep them in their original positions you would have to animate the opacity using .css instead of using fadeIn and fadeOut
My answer is based on this answer from another SO question

How to clear a javascript timeout thats set within a function

I have a recursive type function in Javascript that runs like this:
function loadThumb(thumb) {
rotate=setTimeout(function() {
loadThumb(next);
}, delay);
}
Note: I've simplified the function to make it easier to read.
I have "a" tags called like this
Load thumb 3
However, they don't clearout the timer, the timer continues to cycle through the function irregardless of the clearTimeout() being called.
Any ideas why? I think it might have something to do with a scope problem or something like that.
Yeah, you need to make rotate a global variable. Simply declare it outside the function like so:
var rotate;
var delay = 1000;
function loadThumb(thumb) {
alert("loading thumb: " + thumb);
rotate = setTimeout(function() {
loadThumb(thumb + 1);
}, delay);
}
Also, you need to make sure you clear the timeout before you call loadThumb. Otherwise you'll clear the timer you just started.
Load thumb 3
fiddle: http://jsfiddle.net/63FUD/
it may be the issue of scope so make rotate as global variable and call clearTimeout(rotate);
refer clearTimeout() example
It may be a scoping issue if you are not declaring rotate externally.
Try this:
var rotate = 0;
function loadThumb(thumb) {
rotate=setTimeout(function() {
loadThumb(next);
}, delay);
}
Return false on the link
Since you are not using var rotate, it should not be a scoping issue since rotate would be in the window scope. Can you show the complete code?
It is considered poor coding to inline the script - you should attach the event handler onload of the page
Also you should not have the setTimeout inside a function that might be called for one image
Try this:
var rotate,next=1;
function loadThumb(thumb) {
if (thumb) ... use thumb
else ... use next
}
function slide() {
rotate=setInterval(function() {
loadThumb();
next++;
if (next>=images.length) next=0;
}, delay);
}
window.onload=function() {
var links = document.getElementsByTagName("a");
if (links[i].className==="thumbLink") {
links[i].onclick=function() {
var idx = this.id.replace("link","");
loadThumb(idx);
clearInterval(rotate);
return false;
}
}
document.getElementById("start").onclick=function() {
slide();
return false;
}
document.getElementById("stop").onclick=function() {
clearInterval(rotate);
return false;
}
slide();
}
assuming
Start
Stop
Show 1
Show 2
Show 3
If you have to manage multiple timeouts, you can use an object in the global scope and some custom methods to create and remove your timeouts. To access the methods you can either put the calls in the onclick handler of your links (like in the example), or use a library like jQuery to bind them.
<script type="text/javascript">
var timeouts = timeouts || {};
function createTimeout(name, milliseconds, callback) {
timeouts.name = setTimeout(callback, milliseconds);
}
function removeTimeout(name) {
if (typeof(timeouts.name) !== undefined) {
clearTimeout(timeouts.name);
timeouts.name = undefined;
}
}
createTimeout('foo', 5000, function() {
alert('timeout')
});
</script>
i have also posted an example on jsFiddle http://jsfiddle.net/AGpzs/
I'm not sure what exactly you are doing, because as far as I can see you didn't post all the code, but this looks better for me:
function loadThumb(thumb) {
return setTimeout(function() {
loadThumb(next);
}, delay);
}
and then:
Load thumb 3

Categories