I'm trying to leverage the jquery queues in order to execute a series of functions in order. These "queued" functions, actually animate objects around on a page.
this was my original code for building my queue:
this.queue = function(fx, params) {
this.animation_queue.push({fx: fx, params: params});
};
Then, once all functions are queued, I consume it something like this:
this.animate = function() {
for(var i=0; i<this.animation_queue.length; i++) {
this.animation_queue[i].fx.apply(this, [this.animation_queue[i].params]);
}};
The problem, obviously, that I ran into is that the queue elements were not executing properly. Though they did execute sequentially, because each function performed an animation, I actually need each queue elements to execute after the previous element's animation is finished.
I looked in to using jquery queues, like so:
this.queue = function(fx, params) {
this.animation_queue.delay(500, 'animations');
this.animation_queue.queue('animations', function(next) {
next();
});
};
But I'm not sure how I can call my functions as I did before, with the parameters.
Any suggestions about how I can accomplish this?
According to the doc:
http://api.jquery.com/queue/
You needn't worry about building and managing your own queue, the queue is managed by jQuery the moment you call queue on the element.
Here is a fiddle of their example that works just fine:
http://jsfiddle.net/7GxwR/
So in order to feed your animations onto the queue, just create a single function that will stack all the animations on to the element in order and then call .queue(...) on the element to force it to execute in a queue.
If you're wanting to dynamically change / build the queue you would need to stop all animations, build the runIt function in the fiddle using a loop over an array of dynamic JSON objects that stores your animation parameters and timings, then call showIt and runIt as shown in the fiddle and example in the jQuery API.
The basic point being here, don't build your own queue / array, jQuery will do this for you if you call queue on an element with multiple effects applied already.
Hope this helps.
Related
Suppose I have this code:
$('button').click(function onClick() {
$('#divResult').text(Math.rand());
});
setInterval(function timeout() {
console.log("Hello");
}, 300);
setInterval(function timeout() {
console.log("Hi");
}, 200);
setInterval(function timeout() {
console.log("Yo!");
}, 100);
So basically, after 300ms, I'll have more than 1 callback function in the event queue. Now, let's say that after some period, a user clicks on something. This click event gets processed and the callback function onClick() goes inside the event queue too. Let's say the first time it goes there, there are already 2 callback functions created by setInterval. Since this is a DOM-related function which will do re-rendering of the window, will it have priority over these functions?
In this talk on event loops, the author mentions a render queue which is given a priority over the callback queue (where callbacks by methods like setTimeout and setInterval go). Since onClick() does things related to rendering, will it go into this queue or into the regular callback queue and wait for its turn?
Well I think I'll give it a shot, but this info is pretty new to me as well.
Since this is a DOM-related function which will do re-rendering of the window, will it have priority over these functions?
No, I don't believe that function itself will have any more priority than the functions inside of those intervals. I don't think that the browser itself will know that the click callback will actually change the DOM. After the callback function runs and the element's text changes, then the browser will repaint that node when it has a chance.
Since onClick() does things related to rendering, will it go into this queue or into the regular callback queue and wait for its turn?
I don't believe the onclick handler has to do anything related to rendering. Yes, most of the time we code it to, but I don't think it always has to change an element's appearance. It could just send an Ajax request in the background. It could just log something to the console. You never really know.
Also keep in mind that setInterval doesn't add an event onto the event queue to execute at a certain time later. It waits a certain time and then adds that event onto the event queue. You can read more on that here.
Hopefully I didn't make a fool of myself since I really didn't start diving deep into JS until a few months ago. If I'm off about anything let me know.
I was just messing around on the documentation page of jQuery.promise() and came across the following peice of code :
$("button").on("click", function () {
$("p").append("Started...");
$("div").each(function (i) {
$(this).fadeIn().fadeOut(1000 * (i + 1));
});
$("div").promise().done(function () {
$("p").append(" Finished! ");
});
});
FIDDLE HERE
Now I do understand that $.defer in jQuery assists in Asynchronous programming, also I understand that $.done and $.fail are part of the $promise object .
I have read an interesting article HERE. There are a few good examples of how $.defer can be used to monitor css-3 transitions.
However in the fiddle example I provide, I fail to understand how $.promise picks up the fact that the transition is complete. How does promise pick up that that fadeout() is complete?
How does the below piece of code really work?
$("div").promise().done(function () {
$("p").append(" Finished! ");
});
How is promise really working here? Can anyone explain?
To put it simply, jQuery creates a queue of Deferred objects on each object returned by the $("div") selector (these are visible using the .data() function).
When you add some CSS animations to the divs with jQuery functions such as fadeIn() or fadeOut(), it creates Deferred objects that are appended to each individual div queues.
Using$("div").promise().done() on the parent collection allows to check if all of the children Deferred object queues are empty (jQuery will iterate on the children elements).
I haven't delved into the jQuery source, but here's my understanding.
$.promise returns a Promise which completes once all actions of a certain type have ended. By default, the 'type' is fx (source).
When the fx queue is empty, the promise will resolve.
In your fiddle, you call fadeIn(), which adds the animation to the fx queue. ($.fadeIn() has queue: true by default.) $.fadeOut does the same.
When the queue is empty, the promise will resolve. This fiddle would support that. (Queue is 'inprogress' whilst the animations are running, but empty 100ms later.)
A slightly more convoluted fiddle - notice how the promise completes when we clear the fx queue using $(el).queue('fx',[]);?
I have a function that is bound to mouse click events on a Google Map. Due to the nature of the function it can take a few moments for processing to complete (.1sec - 2sec depending on connection speeds). In itself this is not much of a problem, however if the user gets click happy, this can cause problems and later calls are a bit depended on the previous one.
What would be the best way to have the later calls wait for previous ones to complete? Or even the best way to handle failures of previous calls?
I have looked at doing the following:
Using a custom .addEventListener (Link)
Using a while loop that waits previous one has processed
Using a simple if statement that checks if previous one needs to be re-run
Using other forms of callbacks
Now for some sample code for context:
this.createPath = function(){
//if previous path segment has no length
if (pathSegment[this.index-1].getPath().length === 0){
//we need the previous path segment recreated using this same function
pathSegment[this.index-1].createPath();
//now we can retry this path segment again
this.createPath();
}
//all is well, create this path segment using Google Maps direction service
else {
child.createPathLine(pathSegment[this.index-1].getEndPoint(), this.clickCoords);
}
}
Naturally this code as it is would loop like crazy and create many requests.
This is a good use case for promises.
They work like this (example using jQuery promises, but there are other APIs for promises if you don't want to use jQuery):
function doCallToGoogle() {
var defer = $.Deferred();
callToGoogleServicesThatTakesLong({callback: function(data) {
defer.resolve(data);
}});
return defer.promise();
}
/* ... */
var responsePromise = doCallToGoogle();
/* later in your code, when you need to wait for the result */
responsePromise.done(function (data) {
/* do something with the response */
});
The good thing is that you can chain promises:
var newPathPromise = previousPathPromise.then(
function (previousPath) { /* build new path */ });
Take a look to:
http://documentup.com/kriskowal/q/
http://api.jquery.com/promise/
To summarize promises are an object abstraction over the use of callbacks, that are very useful for control flow (chaining, waiting for all the callbacks, avoid lots of callback nesting).
I need to perform several functions in my JavaScript/jQuery, but I want to avoid blocking the UI.
AJAX is not a viable solution, because of the nature of the application, those functions will easily reach the thousands. Doing this asynchroniously will kill the browser.
So, I need some way of chaining the functions the browser needs to process, and only send the next function after the first has finished.
The algorithm is something like this
For steps from 2 to 15
HTTP:GET amount of items for current step (ranges somewhere from a couple of hundred to multiple thousands)
For every item, HTTP:GET the results
As you see, I have two GET-request-"chains" I somehow need to manage... Especially the innermost loop crashes the browser near to instantly, if it's done asynchroniously - but I'd still like the user to be able to operate the page, so a pure (blocking) synchronous way will not work.
You can easily do this asynchronously without firing all requests at once. All you need to do is manage a queue. The following is pseudo-code for clarity. It's easily translatable to real AJAX requests:
// Basic structure of the request queue. It's a list of objects
// that defines ajax requests:
var request_queue = [{
url : "some/path",
callback : function_to_process_the_data
}];
// This function implements the main loop.
// It looks recursive but is not because each function
// call happens in an event handler:
function process_request_queue () {
// If we have anything in the queue, do an ajax call.
// Otherwise do nothing and let the loop end.
if (request_queue.length) {
// Get one request from the queue. We can either
// shift or pop depending on weather you prefer
// depth first or breadth first processing:
var req = request_queue.pop();
ajax(req.url,function(result){
req.callback(result);
// At the end of the ajax request process
// the queue again:
process_request_queue();
}
}
}
// Now get the ball rolling:
process_request_queue();
So basically we turn the ajax call itself into a pseudo loop. It's basically the classic continuation passing style of programming done recursively.
In your case, an example of a request would be:
request_queue.push({
url : "path/to/OUTER/request",
callback : function (result) {
// You mentioned that the result of the OUTER request
// should trigger another round of INNER requests.
// To do this simply add the INNER requests to the queue:
request_queue.push({
url : result.inner_url,
callback : function_to_handle_inner_request
});
}
});
This is quite flexible because you not only have the option of processing requests either breadth first or depth first (shift vs pop). But you can also use splice to add stuff to the middle of the queue or use unshift vs push to put requests at the head of the queue for high priority requests.
You can also increase the number of simultaneous requests by popping more than one request per loop. Just be sure to only call process_request_queue only once per loop to avoid exponential growth of simultaneous requests:
// Handling two simultaneous request channels:
function process_request_queue () {
if (request_queue.length) {
var req = request_queue.pop();
ajax(req.url,function(result){
req.callback(result);
// Best to loop on the first request.
// The second request below may never fire
// if the queue runs out of requests.
process_request_queue();
}
}
if (request_queue.length) {
var req = request_queue.pop();
ajax(req.url,function(result){
req.callback(result);
// DON'T CALL process_request_queue here
// We only want to call it once per "loop"
// otherwise the "loop" would grow into a "tree"
}
}
}
You could make that ASYNC and use a small library I wrote some time ago that will let you queue function calls.
OVERVIEW
I'm working on a project and I've come across a bit of a problem in that things aren't happening in the order I want them to happen. So I have been thinking about designing some kind of Queue that I can use to organize function calls and other miscellaneous JavaScript/jQuery instructions used during start-up, i.e., while the page is loading. What I'm looking for doesn't necessarily need to be a Queue data structure but some system that will ensure that things execute in the order I specify and only when the previous task has been completed can the new task begin.
I've briefly looked at the jQuery Queue and the AjaxQueue but I really have no idea how they work yet so I'm not sure if that is the approach I want to take... but I'll keep reading more about these tools.
SPECIFICS
Currently, I have set things up so that some work happens inside $(document).ready(function() {...}); and other work happens inside $(window).load(function() {...});. For example,
<head>
<script type="text/javascript">
// I want this to happen 1st
$().LoadJavaScript();
// ... do some basic configuration for the stuff that needs to happen later...
// I want this to happen 2nd
$(document).ready(function() {
// ... do some work that depends on the previous work do have been completed
var script = document.createElement("script");
// ... do some more work...
});
// I want this to happen 3rd
$(window).load(function() {
// ... do some work that depends on the previous work do have been completed
$().InitializeSymbols();
$().InitializeBlock();
// ... other work ... etc...
});
</script>
</head>
... and this is really tedious and ugly, not to mention bad design. So instead of dealing with that mess, I want to design a pretty versatile system so that I can, for example, enqueue $().LoadJavaScript();, then var script = document.createElement("script");, then $().InitializeSymbols();, then $().InitializeBlock();, etc... and then the Queue would execute the function calls and instructions such that after one instruction is finished executing, the other can start, until the Queue is empty instead of me calling dequeue repeatedly.
The reasoning behind this is that some work needs to happen, like configuration and initialization, before other work can begin because of the dependency on the configuration and initialization steps to have completed. If this doesn't sound like a good solution, please let me know :)
SOME BASIC WORK
I've written some code for a basic Queue, which can be found here, but I'm looking to expand its functionality so that I can store various types of "Objects", such as individual JavaScript/jQuery instructions and function calls, essentially pieces of code that I want to execute.
UPDATE
With the current Queue that I've implemented, it looks like I can store functions and execute them later, for example:
// a JS file...
$.fn.LoadJavaScript = function() {
$.getScript("js/Symbols/Symbol.js");
$.getScript("js/Structures/Structure.js");
};
// another JS file...
function init() { // symbols and structures };
// index.html
var theQueue = new Queue();
theQueue.enqueue($().LoadJavaScript);
theQueue.enqueue(init);
var LJS = theQueue.dequeue();
var INIT = theQueue.dequeue();
LJS();
INIT();
I also think I've figured out how to store individual instructions, such as $('#equation').html(""); or perhaps even if-else statements or loops, by wrapping them as such:
theQueue.enqueue(function() { $('#equation').html(""); // other instructions, etc... });
But this approach would require me to wait until the Queue is done with its work before I can continue doing my work. This seems like an incorrect design. Is there a more clever approach to this? Also, how can I know that a certain function has completed executing so that the Queue can know to move on? Is there some kind of return value that I can wait for or a callback function that I can specify to each task in the Queue?
WRAP-UP
Since I'm doing everything client-side and I can't have the Queue do its own thing independently (according to an answer below), is there a more clever solution than me just waiting for the Queue to finish its work?
Since this is more of a design question than a specific code question, I'm looking for suggestions on an approach to solving my problem, advice on how I should design this system, but I definitely welcome, and would love to see, code to back up the suggestions :) I also welcome any criticism regarding the Queue.js file I've linked to above and/or my description of my problem and the approach I'm planning to take to resolve it.
Thanks, Hristo
I would suggest using http://headjs.com/ It allows you to load js files in parallel, but execute them sequentially, essentially the same thing you want to do. It's pretty small, and you could always use it for inspiration.
I would also mention that handlers that rely on execution order are not good design. I am always able to place all my bootstrap code in the ready event handler. There are cases where you'd need to use the load handler if you need access to images, but it hasn't been very often for me.
Here is something that might work, is this what you're after?
var q = (function(){
var queue = [];
var enqueue = function(fnc){
if(typeof fnc === "function"){
queue.push(fnc);
}
};
var executeAll = function(){
var someVariable = "Inside the Queue";
while(queue.length>0){
queue.shift()();
}
};
return {
enqueue:enqueue,
executeAll:executeAll
};
}());
var someVariable = "Outside!"
q.enqueue(function(){alert("hi");});
q.enqueue(function(){alert(someVariable);});
q.enqueue(function(){alert("bye");});
alert("test");
q.executeAll();
the alert("test"); runs before anything you've put in the queue.
how do I store pieces of code in the Queue and have it execute later
Your current implementation already works for that. There are no declared types in JavaScript, so your queue can hold anything, including function objects:
queue.enqueue(myfunc);
var f = queue.dequeue();
f();
how can I have the Queue do its own thing independently
JavaScript is essentially single-threaded, meaning only one thing can execute at any instant of time. So the queue can't really operate "independently" of the rest of your code, if that is what you mean.
You basically have two choices:
Run all the queued functions, one after the other, in a single go -- this doesn't even require a queue since it is the same as simply putting the function calls directly in your code.
Use timed events: run one function at a time and once it completes, set a timeout to execute the next queued function after a certain interval. An example of this follows.
function run() {
var func = this.dequeue();
func();
var self = this;
setTimeout(function() { self.run(); }, 1000);
}
If func is an asynchronous request, you'll have to move setTimeout into the callback function.
**The main functions**
**From there we can define the main elements required:**
var q=[];//our queue container
var paused=false; // a boolean flag
function queue() {}
function dequeue() {}
function next() {}
function flush() {}
function clear() {}
**you may also want to 'pause' the queue. We will therefore use a boolean flag too.
Now let's see the implementation, this is going to be very straightforward:**
var q = [];
var paused = false;
function queue() {
for(var i=0;i< arguments.length;i++)
q.push(arguments[i]);
}
function dequeue() {
if(!empty()) q.pop();
}
function next() {
if(empty()) return; //check that we have something in the queue
paused=false; //if we call the next function, set to false the paused
q.shift()(); // the same as var func = q.shift(); func();
}
function flush () {
paused=false;
while(!empty()) next(); //call all stored elements
}
function empty() { //helper function
if(q.length==0) return true;
return false;
}
function clear() {
q=[];
}
**And here we have our basic queue system!
let's see how we can use it:**
queue(function() { alert(1)},function(){ alert(2)},function(){alert(3)});
next(); // alert 1
dequeue(); // the last function, alerting 3 is removed
flush(); // call everything, here alert 2
clear(); // the queue is already empty in that case but anyway...