just trying to get to grips with chaining asynchronous functions together as im learning to use Parse.com
I have a function thats like:
delete_post(post_id)
.then(function (){
_this.show_posts(thread_id);
}).then(function (){
_this.clear_submit_forum();
})
and it works ok! now when i add a preceding function so it looks like :
show_confirmation_alert("are you absolutely sure you want to delete the post?")
.then(function(){
delete_post(post_id);
}).then(function (){
_this.show_posts(thread_id);
}).then(function (){
_this.clear_submit_forum();
})
my show confirmation function does this sort of thing:
$popup = create_popup(message)
var def = new $.Deferred();
$popup.find(".ok").bind("click", function (){
def.resolve();
});
$popup.find(".cancel").bind("click", function(){
def.reject();
});
$popup.popup();
$popup.popup("open");
return def.promise();
but when i click ok on the popup all the functions start running and therefore its no use. I really want to have a popup that call a chain of functions without adding handler every time it will make my life so much easier. what could possibly cause this. Is there something i'm really missunderstanding about promises? I thought the function in then cannot run until it recieves a resolved promised from the preceding function so why on earth are they not chaining in series? Thanks
Related
So I have this problem. I'm fairly new to angular and I've been told to modify a directive which manages forms to make the submit button disabled then enabled again when all the work is done.
Since the functions being called usually have async calls, simply adding code sequentially doesn't work.
var ngSubmit = function() {
vm.disabled = true;
$scope.ngSubmitFunction();
vm.disabled = false;
}
Button is enabled before async calls under ngSubmitFunction() finish.
So I thought a promise would fix that and made something like:
var promise = function() {
return $q(function (resolve) {$scope.ngSubmitFunction()});
}
var ngSubmit = function() {
vm.disabled = true;
promise().then(function() {
vm.disabled = false;
});
}
This doesn't output any error but never enables the button again (.then is never called).
Tried different kind of promises declaration, all with the same result, except for this one:
$scope.submitPromise = function() {
return $q.when($scope.ngSubmitFunction());
}
This does call .then function, but again, doesn't wait for any child async function to finish. '.then' is called instantly, like the sequential version.
Have in mind that I don't know what's under ngSubmitFunction(). It is used by dozens developers and it may contain from 0 to multiple async calls. But typical scenario is something like:
ngSubmitFunction() calls func()
-- func() decides wether to call create() or update()
-- -- update() calls a elementFactory.update() which is an async call
-- -- -- elementFactory.update().then(function()) is called when finished.
-- -- -- -- At THIS point, I should enable the button again.
How can I achieve this? is there a way to chain promises with non-promises functions in between? or another way to make a code only execute when everything else is done? I thought about creating an event at DataFactory when an async call is over but if the update() function was calling to more than one async call this wouldn't work.
If you are using promises, your async functions should return promises, if they do it should work like this:
var ngSubmit = function() {
vm.disabled = true;
$scope.ngSubmitFunction().then(() => {
vm.disabled = false;
});
}
I don't know what's under ngSubmitFunction()
Then you're out of luck, promises won't help you here. Promises or $q.when cannot magically inspect the call and see what asynchronous things it started or even wait for them - ngSubmitFunction() needs to return a promise for its asynchronous results itself.
You need to make every function in your codebase which (possibly) does something asynchronous that needs to be awaitable return a promise. There's no way around this.
Well, in case someone wonders, we haven't found a solution to that (there probably isn't). So we will go with the adding returns to all the chain of functions to make sure the ngSubmitFunction recieves a promise and therefor can wait for it to finish before calling '.then'. Not only this makes it work for the cases where there's only one promise implied but it is also a good programming practice.
Cases with more than one promise are scarce so we will treat them individually on the controller itself.
Thank you all for your comments.
I can’t get my head around some of the promises. They work fine in some instances of my app, but in some they just never work.
In my controller, I have the following command:
myFactory.redrawCategories()
.then(myFactory.redrawTasks());
The redrawTasks function is called instantly, without waiting for the redrawCategories to finish.
The functions inside my factory look like this
redrawTasks: function(){
var defer = $q.defer();
db.getAllDocs("task_").then(function(res) {
angular.forEach(res, function(value){
value = value.doc;
tasks[value.category].push(value);
});
angular.forEach(tasks, function(taskArray, cat){
// some code
});
defer.resolve(1);
});
return defer.promise;
},
The other one is like
redrawCategories: function(){
var deferred = $q.defer();
db.getAllDocs("category_").then(function(res) {
var categoryArray = [];
angular.forEach(res, function(value){
categoryArray.push(value.doc);
});
deferred.resolve("done");
});
return deferred.promise;
},
Some of the unimortant code has been removed for better overview.
No idea really how to do it. I've tried putting the resolve() function just in front of the return but that doesn't work either.
I've read that sometimes you have to wrap things in a $scope.$apply, well in this case most likely a $rootScope.$apply as it's in the factory outside of the controller scope, but that doesn't really change it either, besides I haven't really grasped when something is "outside of Angular" as they describe that.
I've read a lot of examples and tutorials, but I just don't see the forest for the trees anymore.
Any help would be appreciated a lot. I'm so stuck with this :/ Thanks
.then expects a function reference.
myFactory.redrawCategories()
.then(myFactory.redrawTasks());
should be
myFactory.redrawCategories()
.then(myFactory.redrawTasks);
When you have the () the function is executed immediately and .then is passed whatever it returned.
As noted in the comments, if you're relying on this in redrawTasks you'd do
myFactory.redrawCategories()
.then(function() {
return myFactory.redrawTasks();
});
Try:
myFactory.redrawCategories()
.then(myFactory.redrawTasks);
You were calling the function. Instead you just want to pass it to then function and have angular call it.
I am not very familiar with callback() functions. I am looking for an explanation and an example of what a proper use case might be. In my example below, how can I utilize a callback and also, should I?
Here I have two functions:
addShape = function () {
ExampleService.createShape(function () { //ajax
shapeMade = true;
//anything else etc.......
});
}
deleteShape = function () {
ExampleService.removeShape(function () { //ajax
shapeMade = false;
//anything else etc.......
});
}
The third function (focus of my question)
resetShape = function () {
deleteShape();
addShape();
console.log('example');
}
When I call the resetShape() function, the example gets logged to the console before both deleteShape() and addShape() have finished.
Would this be a situation to use a callback()? If so, how? If not, why?
Callbacks are used for asynchronous functions. In this case, since you have multiple async function to wait for, you probably want to use a Promise, and Promise.all to execute the callback when all async operations are complete:
resetShape = function () {
Promise.all([ deleteShape(), addShape() ]).then( function() {
console.log('example');
});
}
Note that, for this to work, your functions "deleteShape" and "addShape" have to return the promise object:
addShape = function () {
return new Promise(function(resolve, reject) {
ExampleService.createShape(function () {
shapeMade = true;
resolve();
});
});
A callback is used mainly for two reasons, to give another object or function a way to tell us something we were waiting for has completed (or changed) or a way to get something from us when they require it.
First case: imagine a function that makes an asynchronous request to
a server. We don't know how much time it'll take to complete, so we
give the requester a way to tell us it's done:
getDataFromServer('foo.php', callback); //callback will be called when the request
//is done. We can do something else meanwhile.
Second case. I could create and object that, when needed, will ask
for more data (ie, getting more rows to append to a table when we
scroll down)
var tableBuilder = new TableBuilder(container, dataGetterCallback);
dataGetterCallback will be called whenever TableBuilder needs more rows. It'll have the logic to give it to them (maybe by receiving a parameter that tells it in which index to start).
Hope this gives you an idea.
I'm writing tests for my Node.js/Express/Mongoose project using Mocha and Should.js, and I'm testing out my functions that access my MongoDB. I'm want these tests to be completely independent from the actual records in my database, so I want to create an entry and then load it, and do all my tests on it, then delete it. I have my actual functions written (I'm writing tests after the entire project is complete) such that the create function does not have a callback; it simply just renders a page when it's done. In my tests script, I call my load_entry function after I call create, but sometimes create takes longer than usual and thus load_entry throws an error when it cannot actually load the article since it has yet to be created. Is there any way to make sure an asynchronous function is finished without using callbacks?
Please let me know if there is any more info I can provide. I looked all over Google and couldn't find anything that really answered my question, since most solutions just say "use a callback!"
Use what is known as a promise
You can read more about it here.
There are lots of great libraries that can do this for you.
Q.js is one I personally like and it's widely used nowadays. Promises also exist in jQuery among many others.
Here's an example of using a q promise with an asynchronous json-p call: DEMO
var time;
$.ajax({
dataType: 'jsonp',
type: 'GET',
url: "http://www.timeapi.org/utc/now.json",
success: function (data) {
time = data;
},
error: function (data) {
console.log("failed");
}
})
.then(function(){ // use a promise library to make sure we synchronize off the jsonp
console.log(time);
});
This is definitely the kind of thing you want a callback for. Barring that, you're going to have to write some kind of callback wrapper that polls the database to determine when it has finished creating the relevant records, and then emits an event or does some other async thing to allow the test to continue.
Since the only native way to do asynchronous things are: setTimeout, setInterval and addEventListener, and they all take a callback you will eventually have to use a callback somewhere.
However, you can hide that by using Promises/A, also known as Deferreds.
Your code could look like this:
db.create_entry("foo", data).done(function (entry) {
db.delete_entry(entry).done(function () {
console.log("entry is deleted");
});
});
Using then-chaining:
db.create_entry("foo", data).then(function (entry) {
return db.delete_entry(entry);
}).done(function () {
console.log("entry is deleted");
});;
I found a solution that works. What I did was to add a callback to my function (next) and only call it if it's specified (i.e., for the tests):
//more stuff above
article.save(function(err){
if (!err) {
console.log(req.user.username + ' wrote ' + article.slug)
return next() || res.redirect('/admin')
}
return next(err) || res.render('articles/entry_form', {
title: 'New Entry',
article: article,
})
})
This way, when I run the actual server and no callback is specified, it won't throw an error, because it will simply return the res.render statement.
I'm trying to figure the best way to get my functions executing in the correct order.
I have 3 functions
function 1 - squirts OPTIONs into a SELECT via JSON and marks them as selected
function 2 - squirts OPTIONS into a 2nd SELECT and marks them as selected
function 3 - gets the values from the above SELECTs along with some additional INPUT values, does an AJAX GET resulting in JSON data, which is read and populates a table.
With JQuery Onload, I execute:
function1();
function2();
function3();
I'm finding function3 is executing before the SELECTs have been populated with OPTIONS and hence the table has no results, because the values sent in the GET were blank.
I know this is probably a very simple problem and that there are probably a dozen ways to accomplish this, but basically I need the best way to code this so that function3 only runs if function1 and 2 are complete.
I've come into Javascript via the back door having learnt the basics of JQuery first!
Thanks for your assistance.
Javascript executes synchronously, which means that function3 must wait for function2 to complete, which must wait for function1 to complete before executing.
The exception is when you run code that is asynchronous, like a setTimeout, setInterval or an asynchronous AJAX request.
Any subsequent code that relies on the completion of such asynchronous code needs to be called in such a manner that it doesn't execute until the asynchronous code has completed.
In the case of the setTimeout, you could just place the next function call at the end of the function you're passing to the setTimeout.
In the case of an AJAX call, you can place the next function call in a callback that fires upon a completed request.
If you don't want the execution of the subsequent function to occur every time, you can modify your functions to accept a function argument that gets called at the end of the asynchronous code.
Something like:
function function1( fn ) {
setTimeout(function() {
// your code
// Call the function parameter if it exists
if( fn ) {
fn();
}
}, 200);
}
function function2() {
// some code that must wait for function1
}
onload:
// Call function1 and pass function2 as an argument
function1( function2 );
// ...or call function1 without the argument
function1();
// ...or call function2 independently of function1
function2();
I recommend you use a Promises library. You can hack simple solutions like other answers suggest, but as your application grows, you'll find you are doing more and more of these hacks. Promises are intended to solve these kinds of problems when dealing with asynchronous calls.
The CommonJS project has several Promises proposals which you should check out. Here is a question I asked on SO about Promises a while back with links to other solutions. Learn more about Promises in this Douglas Crockford video. The whole video is good, but skip to just past half way for promises.
I'm using the FuturesJS library currently as it suits my needs. But there are advantages to other implementations as well. It allows you to do sequences very easily:
// Initialize Application
Futures.sequence(function (next) {
// First load the UI description document
loadUI(next); // next() is called inside loadUI
})
.then(function(next) {
// Then load all templates specified in the description
loadTemplates(next); // next() is called inside loadTemplates
})
.then(function(next) {
// Then initialize all templates specified in the description
initTemplates();
});
Even more powerful is when you need to join async events together and do another action when all of the other async events have completed. Here's an example (untested) that will load a bunch of HTML files and then perform an action only once ALL of them have completed loading:
var path = "/templates/",
templates = ["one.html","two.html","three.html"],
promises = [];
$.each(templates, function(i,name) {
promises[i] = Futures.promise();
var $container = $("<div>");
$container.load(path+name, function(response,status,xhr) {
promises[i].fullfill();
}
});
Futures.join(promises, {timeout: 10000}) // Fail if promises not completed in 10 seconds
.when(function(p_arr) {
console.log("All templates loaded");
})
.fail(function(p_arr) {
console.log("Error loading templates");
});
This might be overkill for your application. But if the application is growing in complexity, using promises will help you in the long run.
I hope this helps!
invoke function2 inside of function1 and function3 inside of function2.
It's not clear why f1 and f2 are executing before f3.
Also, are you using the preferred $(document).ready() or some variation of onload?
It might be helpful if you provide a reproducible test case.
fun3() will only run after both are ready. It might run twice. You can fix this with a lock inside fun3() you would need a Singleton to guarantee it works correctly.
var select1ready = false, select2ready = false;
fun1()
{
// do stuff
select1ready = true;
fun3();
}
fun2()
{
// do stuff
select2ready = true;
fun3();
}
fun3()
{
if (select1ready && select2ready)
{
}
}
fun1();
fun2();