I have an Angular.js service which delivers its results asynchronously, after looking around for a while the main pattern for doing this seems to be using $q promises like this
angular.module('fooApp').factory('foo', function ($q) {
var result;
function build() {
var d = $q.defer();
longAsyncInit(function(data) {
result = data;
d.resolve(result);
});
return d.promise;
};
return {
get: function () {
if (result) {
return $q.when(result);
} else {
return build();
}
}
}
});
The problem is that I have a number of services which have this service as a dependency and get is called multiple times before the first longAsyncInit ends (which means that longAsyncInit gets called multiple times, each time creating a new promise). In my case this is unacceptable, I really need longAsyncInit to be called once, no more. I'm currently addressing this issue like this
angular.module('fooApp').factory('foo', function ($q) {
var result
var d;
function build() {
d = $q.defer();
longAsyncInit(function(data) {
result = data;
d.resolve(result);
});
return d.promise;
};
return {
get: function () {
if (result) {
return $q.when(result);
} else if (d) {
return d.promise;
} else {
return build();
}
}
}
});
This means if longAsyncInit is already ongoing when a get() call is made, it returns the current promise, instead of creating a new one and calling longAsyncInit again. This seems to work but feels inelegant and fragile, is there a better way of doing this?
You are looking for debounce method to solve problem.
From Underscore library documentation what _.debounce() do
Creates and returns a new debounced version of the passed function
which will postpone its execution until after wait milliseconds have
elapsed since the last time it was invoked. Useful for implementing
behavior that should only happen after the input has stopped arriving.
For example: rendering a preview of a Markdown comment, recalculating
a layout after the window has stopped being resized, and so on.
Some more explanations
Can someone explain the "debounce" function in Javascript
Some content to read:
http://davidwalsh.name/javascript-debounce-function
Related
I understand that we could use .then to make sure the order of asynchronous calls:
return doTask1()
.then(function () {
return doTask2()
})
But sometimes it will be convenient to have a light and to be able to say: wait and don't execute task2 until a light is set to GREEN; the light is a variable initially set to RED, and can be set to GREEN by task1 or other functions.
Does anyone know if it is possible to accomplish this?
Edit 1: I think being able to express this is particularly useful when we need several tasks to be ended to set the light green, and we don't know/mind the order of these tasks. .then cannot do this easily, because we don't know the order of these tasks.
Edit 2: Regarding my light, I had asked a more specific question. In one word, it is the message another application B sends by postMessage that we are waiting for. At the moment, I have written the following code (which is in a resolve), which works more or less (I have not tried if making only ONE function with their common part will work).
task1: ['codeService', '$window', '$q', function (codeService, $window, $q) {
return codeService.task1().then(function () { // task1 sends a request to another application B by postMessage
var deferred = $q.defer();
$window.addEventListener("message", function (event) {
if (event.data.req === "task1ReturnFromB") deferred.resolve(event.data)
}, { once: true });
return deferred.promise
})
}],
task2: ['codeService', 'task1', '$window', '$q', function(codeService, task1, $window, $q) {
return codeService.task2().then(function () { // task2 sends a request to Application B by postMessage
var deferred = $q.defer();
$window.addEventListener("message", function (event) {
if (event.data.req === "task2ReturnFromB") deferred.resolve(event.data)
}, { once: true });
return deferred.promise
})
}]
So in this specific case, postMessage sent by Application B triggers the event. In a more general case, I guess we could probably trigger an event by eg, dispatchEvent, in one application?
You haven't told us anything about the API for your light so I can only guess what it's like. Presuming it's an event driven model, you can convert it to a promise, and do this:
function waitForGreenLight() {
return new Promise(function (resolve) {
$window.addEventListener("message", function (event) {
if (event.data.req === "task2ReturnFromB") {
resolve(event.data)
}
}, { once: true });
});
}
return doTask1()
.then(waitForGreenLight)
.then(doTask2);
If your light doesn't provide events, you could have a waitForGreenLight that periodically polls it until it's green. The code to use waitForGreenLight would remain the same.
function waitForGreenLight() {
return new Promise(function (resolve) {
var handle = setInterval(function () {
if (myLight.color === 'green') {
resolve();
clearInterval(handle);
}
}, 100);
});
}
Using Promises would be the best way to do this. Promises are objects that execute a function and when that function is done the THEN-function is executed. But only when the function give a result back.
Example
var task1 = new Promise(function(resolve, reject) {
var value = 1;
setTimeout(function(){
resolve(value);
}, 1000);
});
var task1.then(function(value){
// After one second, do something with your value here
});
The closest answer I could find was this https://stackoverflow.com/a/17216555/2834734
The most common use for .then is chaining ajax requests:
$.ajax({...}).then(function(){
return $.ajax({...}); }).then(function(){
return $.ajax({...}); }).then(function(){
return $.ajax({...}); }).then(function(){
return $.ajax({...}); });
this can easily be done in a loop
However it's the looping procedure I'm having difficulty with plus I have some unusual circumstances.
A brief explanation is, I have an array of requests that I need to loop through, some will invoke an ajax load and others will not. I need them to run consecutively but also run a specific function call at then end.
Here is a simple(I hope) sample of my situation:
// Here is my flow of logic
var thingsToDo = new tasks(); // Initiate the constructor, explained below
// Loop through the requests array and process them consecutively
for (var i in thingsToDo.requests) {
$.when(thingsToDo.jqxhr).then(function() {
thingsToDo.requests[i].fn();
})
}
// Run my final function at the end of the chain.
$.when(thingsToDo.jqxhr).then(function() {
runOnceAllComplete();
});
This is the constructor class the above is based on.
// Constructor
function tasks() {
_tasks_ = this; // automatic global var
this.jqxhr = undefined; // Var to monitor ajax calls
this.requests = [ // list of tasks to run.
{
url: 'file1.php',
fn: function() {
_tasks_.load(this.url);
console.log('file1 loaded');
}
}, {
url: 'file2.php',
fn: function() {
_tasks_.load(this.url);
console.log('file2 loaded');
}
}, {
noUrl: true, // Note there is no file to load here
fn: function() {
console.log('no file here to load, but process something else');
$('body').css("background-color", "blue");
}
}, {
url: 'file3.php',
fn: function() {
_tasks_.load(this.url);
console.log('file1 loaded');
}
},
];
this.load = function(file) { // This runs the ajax call and resets this.jqxhr
this.jqxhr = $.get(file);
}
}
function runOnceAllComplete() {
alert('hooray!, we finished');
}
The tricky part I have is the requests are created dynamically so there can be 1-n many requests to perform, which is why I chose to loop, and they must be performed in that order.
As mentioned some requests will invoke an ajax call and others may not, this doesn't seem to break $.when().then(), but the problem is the loop continues before the promise is resolved and my final function happens before the final request.
Still trying to get my head around promises, the first time I've used them.
Try including return statement at fn , this.load ; adding .promise() chained to $("body") at fn to return a jQuery promise object ; using Function.prototype.apply() , $.map() at $.when()
fn: function() {
// added `return`
return _tasks_.load(this.url);
}
this.load = function(file) {
this.jqxhr = $.get(file);
// added `return`
return this.jqxhr
}
fn: function() {
console.log('no file here to load, but process something else');
// added `return` , `.promise()`
return $('body').css("background-color", "blue").promise();
}
$.when.apply($, $.map(thingsToDo.requests, function(task) {
return task.fn()
})).then(runOnceAllComplete)
See also Pass in an array of Deferreds to $.when() , What does $.when.apply($, someArray) do?
however I'm encountering a problem, using the .map() it doesn't wait
for each request to complete before processing the next one. I need
each one to complete before moving to the next.
Try using .queue() , which will calls functions in queue sequentially, and only when next is called at current function
$(thingsToDo).queue("tasks", $.map(thingsToDo.requests, function(task) {
return function(next) {
// call next function in `"tasks"` queue
// when current function completes using `.then(next)`
return task.fn().then(next)
}
})).dequeue("tasks").promise("tasks").then(runOnceAllComplete)
See .queue() , .promise() , Execute function queue in javascript
If we have two function in javascript, one slow and one fast. For example:
function slow() {
setTimeout(function() {console.log("slow finished")}, 10000);
}
function fast() {
console.log("fast");
}
And these functions don't have inside of them new structures like promisses (if we do not implement after).
How can we force these functions run in order? For example:
function run() {
slow();
fast();
}
run();
How can we force fast wait slow finishes?
I'm looking a solution that could work inside mobile application browsers, becase of a Apache Cordova project of mine.
Is there a way to do this?
An idea of mine is inject a callback function between the functions.
And this callback is called at the end of the slow function, calling the fast function.
An important thing is I can't (or would not) rewrite the code of the slow and fast functions,
because they will reside inside external libraries.
I'm looking for a solution to countorn this problem as an external observer and manager.
How can we do this?
Edit
He I was a trying to solve the problem merging the answers. No success yet.
I had changed slow but this is not really allowed. I have changed it to se what is happening with a. I couldn't get something interesting because a becomes undefined immediately and not after slow finishes...
var a = "adsfadsfadsf";
function slow() {
setTimeout(function() {console.log("slow done"); console.log("a2", window.a);}, 3000);
}
function fast() {
console.log("a3", window.a);
console.log("fast done");
}
var newSlow = function() {
return new Promise(function(resolve, reject){
window.a = slow();
console.log("a", a);
resolve("Sucess");
});
};
newSlow().then(function(resolve){fast();}, function(reject){console.log("error");});
I have tried with resolve(slow()); no sucess too.
That's a very interesting question. Well I can think of a way where if it is changing some global variable "g" to some value say "true". In that case if you can run them sequentially as,
<script>
var g = false;
var i;
function slow() {
setTimeout(function() {console.log("slow finished");g=true;}, 10000);
}
function fast() {
console.log("fast");
}
function run() {
slow();
i = setInterval(function(){check();},1000);
}
function check(){
if(g){
fast();
clearInterval(i);
}
}
run();
</script>
As in this demo
UPDATE: Something just struck me and I guess we might be able to add a callback function to slow() even if we can't access it directly.
If a function is called without parenthesis then the entire function as a content is returned as a string so we can edit that string by adding fast() to it registering that string as a function using eval().
function run() {
var myFun = slow+"";
myFun = myFun.substring(0,myFun.length-1);
alert(myFun);
myFun += "fast();}";
//to register the string "myFun" as a function
eval(myFun);
slow();
}
So basically our slow() function becomes,
function slow(){
//function code
//the appended function
fast();
}
NOTE: This will not worked in the example given above where GarouDan has deliberately added setTimeout limit to recreate a scenario where the slow() function takes longer time than the fast() function. However, in a real-world scenario I'm sure this approach would definetly work.
You could use the Promise pattern.
Promises are tailor made for situations where various parts of code may run slow or fast or complete in unknowable amounts of time (or not complete at all), while still giving you execution control.
My personal favorite library that implements the Promise pattern is RSVP.
Here is some pseudocode to give you the idea. Run an operation that may take a long time, then run one only when the first has either completed, or handle it's failure.
function doFoo() {
var promise = new RSVP.Promise(function(resolve, reject) {
// do some long-running operation, like retrieve
// data from a slow site...
if (data.Status && data.Status === 200) {
resolve(data);
} else {
reject(data.Error)
}
});
return promise;
}
function doBar() {
var promise = new RSVP.Promise(function(resolve, reject) {
// do some fast operation, like count to 10
for (i = 0; i < 10; i++) {
console.log(i);
}
resolve("");
});
return promise;
}
Now you can call:
function inOrder() {
doFoo().then(function(success) {
doBar();
}).catch (function(failure) {
console.log("oops! " + failure);
}
});
}
This runs doFoo, and ONLY runs doBar after doFoo has completed successfully. Note that you could also run doBar even if doFoo has failed.
This is my code:
.filter('getUserName', function(User) {
return function(id) {
User.get({ _id: id }, function(user) {
return user.name;
});
};
});
I want the middle function to return user.name. User.get is asynchronous, and so I can only return from the middle function once the inner asynchronous function finishes running.
I know 2 ways to run code once the asynchronous code finishes running: using the success callback, or the success promise. However, both of those create an inner function, hence my problem - I don't know how to run code that
Only runs once the asynchronous function finishes and
Returns from the middle function.
This is a terrible fit for a filter, but just as an intellectual exercise, you could have the filter return some default behavior (i.e. return blank) until data is fetched, and once fetched apply the filter. This would necessitate the filter to be $stateful, which is very wasteful - it will run on every digest cycle.
app.filter("foo", function($timeout){
var cache = {};
function genFoo(input){
$timeout(function(){
cache[input] = input + "foo!";
}, 1000);
}
var filter = function(input){
if (input in cache) return cache[input];
genFoo(input);
return "";
};
filter.$stateful = true;
return filter;
});
Plunker
DO NOT do this as a filter :)
I am getting into programming with javascript and using Promises, right now using Q.js. I have finally gotten to a point where I understand what I am doing, but am having a difficult time with a specific behavior.
I have one situation where I have reasonably similar code repeated several times. It basically goes like this ...
{
// start
var deferred = Q.defer();
// do something {
deferred.resolve();
}
return deferred.promise;
}
Okay, that's all fine and good, but repeating all of this every time was getting annoying, so I attempted to wrap it up in something. This is just an example, it is not the entire javascript file, since most of the other parts are not relevant.
{
var list = [];
queue = function(f) {
var deferred = Q.defer();
list.push(f(deferred));
return deferred.promise;
}
{
queue(function(deferred){
// do some work
// we want the deferred here so we can resolve it at the correct time
deferred.resolve();
});
}
}
The problem is that I don't want this to run the instant I queue it up. I basically want to build the list, and then run it later. I am running the list using the reduce function in Q.js
{
return list.reduce(function(i, f) {
return i.then(f);
}, Q());
}
But this is kind of counter to my goal, since I really don't intend to run them at the same time they are queued. Is there a way to save the execution for later and still pass the deferred object through the function?
Update
I was asked what I expect the code to do, which is a fair question. I'll try to explain. The purpose of this is to split up the logic because I am using ASP.NET MVC, and so I have _Layout pages, and then normal views - so there is logic that cannot run until other things are completed, but some times that is on a per-page basis. This method was contrived to deal with that.
Essentially it works like this ...
Loader.js
This is, for lack of a better term or current implementation, a global object. I have plans to change that eventually, but one step at a time.
{
var Loader = {};
var list = [];
initialize = function() {
Q().then(step1).then(step2).then(process).then(finalStep);
};
queue = function(f) {
// push the given function to the list
};
process = function() {
return list.reduce(function(i,f){
return i.then(f);
}, Q());
};
step1 = function() { // generic example
// create a promise
return deferred.promise;
}; // other steps are similar to this.
return Loader;
}
_Layout
<head>
#RenderSection("scripts", false)
<script type="text/javascript">
// we have the loader object already
Loader.initialize();
</script>
</head>
Index
#section Scripts {
<script type="text/javascript">
Loader.promise(function(deferred){
// do something here.
deferred.resolve();
}));
</script>
}
You could use a closure.
queue(function(deferred) {
return function() {
// this is the actual function that will be run,
// but it will have access to the deferred variable
deferred.resolve();
};
});
I think you should do something like
var Loader = {
promise: function(construct) {
var deferred = Q.defer();
construct(deferred);
return deferred.promise;
},
queue: function(f) {
this.ready = this.ready.then(f);
},
ready: Q.Promise(function(resolve) {
window.onload = resolve; // or whatever you need to do here
// or assign the resolve function to Loader.initialize and call it later
})
};
Then Loader.queue() functions that return other promises.