Hook into the end of an asynchronous javascript call - javascript

I'm calling a javascript function provided by a third-party. This function fires off a few ajax/asynchronous requests before returning, and (impolitely) neither allows me to pass a callback, nor returns any references to the asynchronous calls it is making.
Is there any way I can trigger something when all of the calls it's making complete?
Is there a way I can track 'calls' launched from a javascript function? Forgive my terminology; I'm a javascript beginner.

Is this a library that's loaded from an external source, or do you have your own local copy? In the latter case, you could patch the code to do what you want. Otherwise, I don't really see a way to directly intercept the calls.
If you know exactly what state changes occur for each completed async call, you could set a function to execute on an interval (setInterval(fn, millis)) and check whether all those states have been met. Then, you could fire off some sort of final function to indicate completion.
ie:
var completionHandler = function() { /* do something awesome */ }
var checkCompletion = function() {
if (state1 && state2 && state3) {
clearInterval(interval); // make sure you clear the interval.
completionHandler();
}
}
var interval = setInterval(checkCompletion, 200);

If your third party script is based on jQuery you could try to modify the ajax behaviour with $.ajaxSetup(), set a context object and bind a ajaxComplete handler to this object. But that might mess up the third party library.

Related

"Run if I haven't been called in x seconds" in JavaScript

Suppose I have a callback firing perpetually as the result of some event; i.e. Someone's moving a mouse.
I'd like to run a cleanup action if the callback hasn't fired in x seconds; i.e. If they haven't moved the mouse in 2 seconds, fire.
I think I could probably fix something up with setTimeout, but I'm wondering if any standard libraries have a function for this? Sort of a 'dead-mans-switch', seems like it would be common enough to have a standard method. If not I'm making one. Anyone?
De-bouncing may be a technique that will help.
It is essentially a method of wrapping a function so that you have control over when the wrapped function will execute, regardless of how often the debounced version is called.
This is most commonly used for events, like window resize. Then you can only execute your handler once the user has finished resizing the window rather then whilst they are resizing it.
There is also throttling, this is similar but has important differences.
Throttled functions will execute once every n time rather than a debounced version which will executed after it hasn't be called for n time.
underscore and lodash have implementations of de-bouncing and throttling.
However they it is quite easy to achieve and you don't really need a large library if its not already being used.
I think you're on the right track about setTimeout. As per your wonder, I am not aware of a module that would do it. And due to the intrusive nature of this process, it makes sense.
You could do this tho:
var yourmodule; //assuming you're using a module to store your app code; the object should obviously exist before continuing
yourmodule.cleanupSequenceId = -1;
function yourEventCallback() {
if (yourmodule.cleanupSequenceId !== -1) clearTimeout(yourmodule.cleanupSequenceId);
//function logic
//cleanup:
yourmodule.cleanupSequenceId = setTimeout(cleanupMethod, 2000);
}
After stumbling upon this (very old) question, and reading many others like it, I found a solution that works for me so I wanted to share it.
You define a "Debounce" function like this:
var debounce_timeout // Global debouncer timer, so all calls target this specific timeout.
function debounce(func, delay = 2000) {
clearTimeout(debounce_timeout)
debounce_timeout = setTimeout(() => {
func()
}, delay)
}
Now if you wish to debounce some function, you do:
debounce(myFunction)
Debouncing essentially means, that when your function is called, we observe for 'delay' duration, if any other calls to the function is made. If another call is made, we reset our observing time.

window.location not evaluating until entire call stack is processed?

I have a bit of javascript, triggered from an HTML button, that calls a function. This is using Jquery as well, so there are a couple of underlying functions from that that get called in this process, too. In my script I make a couple of changes to window.location in order to communicate to a remote system (which is supposed to fire off different scripts in response to these calls). This window.location definition is not using the HTTP protocol, but FMP, a registered - on my machine anyway - protocol for FileMaker Pro.
Sample code:
function compareJSON() {
dataSession=({ //build object for output });
$.each( dataSession.chapters , function( indexC, value ) {
//compare objects to some others, testing and changing data
});
//Call remote script on other system
window.location= "fmp://blah.dee.com/Blar?script=SaveJSON&$JSONobject=" + JSON.stringify( dataSession );
//Call remote script on other system
window.location="fmp://blah.dee.com/Blar?script=EditJSON";
}
(Keep in mind, since this is using Jquery, that simply pressing the button that calls this compareJSON() function creates a stack of 2 or 3 other functions before running my function. But, even if it were being called directly in some manner, the compare function itself would be on the stack and thus window.location wouldn't get evaluated until the end of that function.)
The problem is that it looks like the Window.Location isn't being finalized/set/sent/whatever until the ENTIRE JS call stack is finished. So, when I click the button that starts these function calls the stack gets a few Jquery functions put on it (e.g. 'handler', 'default', 'each loop'...), then it hits the JS code that I wrote, which in turn adds a few more function calls to the stack; and then there are a few more Jquery functions that added to the stack, etc. But these stacked window.location definitions made in my functions don't actually trigger the remote system until I step all the way through the JS call stack and exit everything. So the window.location is only defined/set to be whatever was last set in the function calls, instead of including all the intervening definitions/sets that occurred in the stack. It's like a variable that gets changed multiple times in the call stack but only gets read once at the end.
Is there a way to force window.location to be evaluated when it is set instead of waiting for whatever the last setting was?
Thanks,
J
You may want to use an iframe:
function callScript(url) {
var ifr = document.createElement('iframe');
ifr.src = url;
// you can even add ifr.onload = function() {doSomething();}; if you want
}
This will allow any number of calls at once.
This might not work, but the timeout idea is to change something like this:
// code code code ...
window.location = newUrl;
// more code ...
into:
// code code code ...
window.location = newUrl;
setTimeout(function() {
// more code ...
}, 1);
That allows the browser an interval in which it can do something before starting the next event loop for the timer handler.

Run js when document is changed?

I tried looking for the answer, and this is my first post, so bear with me if I mess up in some way.
Basically my problem is this: I'm writing an extension for Chrome that uses jQuery. I have another extension that makes a timed $.ajax() request every 10 seconds. I need to find a way to run my code every time that timed ajax request and its callback function completes. Setting a timer for my own script can be done, although that's rather half-assed and doesn't work as well.
The problem can be illustrated thus:
//extension 1
function timedFunc() {
setTimeout(doStuff, 10000);
};
timedFunc();
//extension 2
//code to be run every time doStuff completes
I feel like there may be a very elementary solution to this problem but I appreciate the help.
There is (was) an event called DOMSubtreeModified.
But it has been deprecated so at tho moment there are really only workarounds available.
Why is the DOMSubtreeModified event deprecated in DOM level 3?
I can't advise on using this event as it hasn't even been implemented in all browsers.
But what you can do (easily) is just trigger you own event with all your ajax call!
Example:
fire your event when (any) ajax call completes:
$(document).ajaxComplete(function() {
$(document).trigger('domChanged');
}
and listen to it:
$(document).on('domChanged',function() {
alert("i changed the DOM tree!");
});
btw:
taken that you just want to react to ajax calls compleing... just use the .ajaxComplete() event:
http://api.jquery.com/ajaxComplete/
i didn't really understand what you are trying to say but i did understand your question in the title so here is my modest answer:
// a global variable for the documents content
var content=document.documentElement.innerHTML;
// return true if the document content has changed
function documentChanged(){
return content==document.documentElement.innerHTML;
}

Having a callback when a function is called without touching the actual function (jquery)?

I'm developing a small plugin that changes the favicons if there are unread messages in mailbox in Roundcubemail. However, the API sucks, and the event listupdate is fired only when the whole page is loaded, even if it is meant to fire when the list is updated.
However, I've managed to find out, that every time the list is updated, certain functions are called, such as set_unread_count. It gets the unread-count easily, so it would be great to somehow "append" stuff to this function. I just think based on hours of searching that there is no solution for this. Can I add a callback to be called when the set_unread_count is called? Can I somehow append stuff to that function? Any other ideas?
Create a little hook.
var _old_set_unread_count = set_unread_count;
set_unread_count = function() {
// do whatever you want here
// access arguments[x] to get arguments.
_old_set_unread_count.apply(this, arguments);
};
Demo: http://www.jsfiddle.net/4yUqL/69/

How can I run some code on all the nodes in a tree?

I want to run some code on all my treeView nodes depending on a value returned from the database and repeat this until a certain value is returned.
I was thinking that:
Give all my tree nodes the same css class so I can access them from JQuery
have a timer in my JQuery function that used ajax to go to the database, when a certain value is returned then stop the timer
Two questions here. How can I make my function run for each of the nodes and how do I do a timer in JavaScript, so:
$(function(){
$('cssClassOfAllMyNodes').WhatFunctionToCallHere?((){
//How do I do Timer functionality in JavaScript?
ForEachTimeInterval
{
//use Ajax to go to database and retrieve a value
AjaxCallBackFunction(result)
{
if (result = 1)
//How to stop the timer here?
}
}
});
});
Hope i'm clear. Thanks a lot
thanks a lot for the answer. And i would like you to comment on the design.
Bascially what i'm trying to acheive is a Windows Wokflow type functionality where each node in my tree updates its image depending on its status, where its status is got from querying the database with a key unique to the tree node. I'm open to ideas on other ways to implement this if you have any. thanks again
Without commenting on your design you can refer to these
$.each()
setTimeout() or setInterval()
You can do:
$(function(){
$('cssClassOfAllMyNodes').each(function (){
// Do something with "this" - "this" refers to current node.
});
});
Te proper way to handle timers in JS is to have a reference to each timeout or interval and then clearing them out.
The difference between them is:
The timeout will only run once, unless stopped before;
The interval will run indefinitely, until stopped.
So you can do something like:
var delay = 2000; // miliseconds
var timer = setTimeout("functionToBeCalled", delay);
clearTimeout(timer); // whenever you need.
Please note you can pass a string to setTimeout (same with setInterval) with the name of the function to be called. Or you could pass a reference to the function itself:
var callback = function () { alert(1); };
var timer = setTimeout(callback, delay);
Be sure not to set an Interval for AJAX requests, because you response might be delayed and successive calls to the server could eventually overlap.
Instead, you should call setTimeout and when the answer arrives then call setTimeout again.

Categories

Resources