I'm using q promises, and I want to show the spinners when promise starts. Currently I'm doing this way:
getPromise().then(function() { spinner.hide() })
and in the getPromise() fn, I'm showing up the spinner, so getPromise looks like:
function getPromise()
{
spinner.show()
}
But is there any way to intercept the then block in q, so that I can add the spinner.show to that intercept?
You are over-thinking it
var spinOnPromise = function(p) {
spinner.show()
p.finally(function() {
spinner.hide()
});
return p;
}
Pass in the promise, and the spinner will go as long as the promise is pending.
Edit: you could do this:
var spinOnPromise = function(p) {
spinner.show()
return p.finally(function() {
return spinner.hide()
});
}
If you do this this, the difference is, if spinner.hide() returns a promise (call it p1), the promise returned from spinOnPromise() will not be resolved until p1 is resolved, but it will resolve to the same value as p. See here for details.
You could do this, but I don't see offhand why you would.
Related
I have the following javascript function :
function render(id) {
var deferred = $q.defer();
Flights.get(id).then(function(flightDto){
Arrivals.getDemoProfile(flightDto.id).then(function(arrivalDto) {
self.arivalId = arrivalDto.id;
deferred.resolve(self);
});
});
return deferred.promise;
}
Is there any way I can simplify better using promise so that the promise only resolves after the arrivals call is made? I am using angular and the built in $q library.
function render(id) {
return Flights.get(id).then(function(flightDto) {
return Arrivals.getDemoProfile(flightDto.id).then(function(arrivalDto) {
self.arivalId = arrivalDto.id;
return self;
});
});
}
Anything you return inside a then will be treated as the resolve of that promise.
Unless you return a promise, in which case that will be waited for, and the result will be treated as the resolve of that promise.
This means you can nest the then as deeply as you need and just keep returning from the nested functions.
The great thing about promises is that they can be chained instead of being nested. This makes the code a lot clearer and easier to reason (about which comes first for example). Following is the code from Buh Buh's answer, improved to chain the second promise instead of nesting:
function render(id) {
return Flights.get(id)
.then(function(flightDto) {
return Arrivals.getDemoProfile(flightDto.id);
})
.then(function(arrivalDto) {
self.arivalId = arrivalDto.id;
return self; // I AM NOT SURE ABOUT THE USEFULNESS OF THIS LINE...
})
;
}
I've been trying to write better code on my node.js server and after reading some blog posts like the following:
http://www.codelord.net/2015/09/24/$q-dot-defer-youre-doing-it-wrong/ (angular specific but same concept)
http://bahmutov.calepin.co/linking-promises.html
I am still not sure if I'm returning my data down the promise chain the "right way".
I cannot tell when it's appropriate to return or pass data down a promise like this
case 1
var promise = function () {
var defer = q.defer();
var myData = "hi"
defer.resolve(myData);
return d.promise;
};
or like this.
case 2
var promise = function () {
var myData = "hi"
return myData;
};
I'm assuming is that if I know something will be returned where it's not possible for the promise chain to break then use case 2 but if their is a change it could fail (i.e. it's returning data from a network call but the user is offline) then use case 1 so it can handle both cases. Is that correct or do I have a misunderstanding about how this flow/process works.
In both cases you are returning a result which is instantly known or computed, while you are wrapping it in a promise in the first case. Whether or not you want to do this depends on whether it should be transparent for the caller of your function if the result is computed asynchronously or not. If you return a promise you are free to change the implementation of your function later to compute or retrieve the result asynchronously.
Two hints:
You are not using a promise in case 2 so don't call your function promise.
In the first case you can just return q("hi"); to wrap the literal in a promise.
promise is for those who engage callback hell, which means your jobs are Asynchronous and chained
for a simple case like $.ajax().success(callback1).fail(callback2) is type of promise
3.your case is not Asynchronous, it might be like this:
var promise1 = function() {
//async get file name
}
var promise2 = function(filename) {
var defer = q.defer();
var myData = "hi"
//async
fs.readFile(filename, 'utf-8', function(err, data) {
if (err) {
defer.reject(err);
} else {
defer.resolve(myData);
}
}
}
return d.promise;
};
var promise3 = function(data) {
//use data do something
}
var onError(err) {
console.log(err);
}
var onDone(result) {
console.log(result)
}
//your logic might look like
promise1.then(promise2).then(promise3).catch(onError).done(onDone);
I would like to return the value of a second promise if the first (value in cache) fails.
I have the following code, but resolve is not defined.
exports.getConfig = function (a, r) {
return new Promise(resolve, reject) {
getConfigFromCache(a, r)
.catch(function(e){
getRouteConfigFromWeb(a, r)
}).then(function(result) {
//return value of the promise that was called
resolve(result)
})
}
};
Assume that both getConfigFromCache and getRouteConfigFromWeb return promises correctly.
Is there a way to accomplish this, or am I thinking through it incorrectly?
You shouldn't need to create a new Promise at all:
exports.getConfig = function (a, r) {
var cache = getConfigFromCache(a, r);
return cache.catch(function(e) {
return getRouteConfigFromWeb(a, r); // NB: return *essential*
});
}
If the getConfigFromCache() call succeeds, the resulting resolved Promise should skip through the .catch and get returned directly.
If the cache call fails, the Promise returned from getRouteConfigFromWeb() is returned instead.
I note also that the very first line of your question actually gives the solution: "I would like to return the value of a second promise if the first (value in cache) fails." - you never actually put a return in the .catch block!
I need to throw in a userID param in the middle of a promise chain(it is the only promise that needs it). All the promises should execute in a synchronous order.
SideNote- All the similar examples on stackoverflow are all a bit different- like using lambda functions(I use declared functions).So I'm still not quite sure.
var initialize = function(userID) {
var firstPromise = Module2.getFirstPromise();
firstPromise.then(getSecondPromise)
.then(getThirdPromise)
.then(getFourthPromise) //<----Fourth promise needs userID
.then(getFifthPromise)
.then(Utils.initializeComplete);
}
All the promises are functions that look like this:
var thirdPromise = function() {
return new Promise(function(resolve, reject) {
//fetch data
//On Return, Store it
resolve() //Nothing needed to passed down from this promise
});
});
}
I'm trying this, and it "works", but I'm not sure if that is how I am "suppose" to handle something like this. :)
var initialize = function(userID) {
var firstPromise = Module2.getFirstPromise();
firstPromise.then(getSecondPromise)
.then(getThirdPromise)
.then(function(){ return fourthPromise(userID)})
.then(getFourthPromise)
.then(Utils.initializeComplete);
}
Note: getFirstPromise is coming from a different module in my code. That shouldn't be important to the question though :)
Assuming that firstPromise is really a promise but secondPromise and so on are actually functions returning promises, then yes, what you've done is how you're supposed to do that. Here's a live example on Babel's REPL, which looks like this:
function doSomething(userID) {
getFirstPromise()
.then(getSecondPromise)
.then(getThirdPromise)
.then(() => getFourthPromise(userID))
// Or .then(function() { return getFourthPromise(userID); })
.then(getFifthPromise)
.catch(err => {
console.log("Error: " + err.message);
});
}
doSomething("foo");
function getFirstPromise() {
console.log("getFirstPromise called");
return new Promise(resolve => {
setTimeout(() => {
resolve("one");
}, 0);
});
}
// and then second, third, fourth (with user ID), and fifth
(If you don't use arrow functions, just replace them with the function form.)
Note the catch in the example above. Unless you have a really good reason not to, a promise chain should always have a .catch if you don't return the result.
Your solution is perfectly fine. It might be easier to understand with concrete signatures.
If your thirdPromise doesn't take anything and doesn't return anything its signature might be written (pseudocode assuming a -> b is a function from a to b) as _ -> Promise (_). If it returns some value a, it would be _ -> Promise (a). If it took something and returned something it might be a -> Promise (b)
So you can reason about your promise chains as about functions taking some value and returning some other value wrapped in a promise. However, your fourthPromise looks differently:
fourthPromise : UserId -> a -> Promise (b)
Which can be written as:
fourthPromise : UserId -> (a -> Promise (b))
It takes one parameter before becoming an actual promise you can chain. In a way, it's a template of a promise.
If you want the plain .then chain in the main function, try to write getFourthPromise as a factory function
function getSomethingByUserID(userId) {
return function() {
return new Promise(function(resolve) {
//your actual async job
resolve('a result');
});
};
}
Then you will get plan list of thens
var firstPromise = Module2.getFirstPromise()
.then(getSecondPromise)
.then(getSomethingByUserID(userId))
.then(Utils.initializeComplete);
Do not forget that if you missed to provide userId to getSomethingByUserID, it will not work.
I'm trying to get my head around promises in JavaScript (in particular AngularJS).
I have a function in a service, let's call it fooService, that checks if we've loaded some data. If it has, I just want it to return, and if we haven't, we need to load the data and return a promise:
this.update = function(data_loaded) {
if (data_loaded) return; // We've loaded the data, no need to update
var promise = Restangular.all('someBase').customGet('foo/bar').then(function(data) {
// Do something with the data here
}
return promise;
}
I have another function that then calls the update function of fooService like so:
fooService.update(data_loaded).then(function() {
// Do something here when update is finished
})
My issue here is that if we don't need to load the data in the update function, a promise isn't returned, so the .then() is not called in my other function. What should the approach be here - basically I want to return a resolved promise immediately from the update() function if we do not need to get data from the Restangular call?
As your promise use the same syntax as the JavaScript native one, you could use and return an already resolved JavaScript promise : Promise.resolve()
return(Promise.resolve("MyReturnValue"));
The current accepted answer is overly complicated, and abuses the deferred anti pattern. Here is a simpler approach:
this.update = function(data_loaded) {
if (data_loaded) return $q.when(data); // We've loaded the data, no need to update
return Restangular.all('someBase').customGet('foo/bar')
.then(function(data) {
// Do something with the data here
});
};
Or, even further:
this._updatep = null;
this.update = function(data_loaded) { // cached
this._updatep = this._updatep || Restangular.all('someBase') // process in
.customGet('foo/bar'); //.then(..
return this._updatep;
};
AngularJS's $q service will help you here. It is much like Kris Kowal's Q promise library.
When you have an async method that may return a promise or value use the $q.when method. It will take what ever is passed to it, be it a promise or a value and create a promise that will be resolved/rejected based on the promise passed, or resolved if a value is passed.
$q.when( fooService.update(data_loaded) ).then(function(data){
//data will either be the data returned or the data
//passed through from the promise
})
and then in your update function return the data instead of just returning
if (data_loaded) return data_loaded;
Similar to Elo's answer, you can return an already resolved promise using the async/await syntax:
this.update = async (data_loaded) => {
if (data_loaded)
return await null; // Instead of null, you could also return something else
// like a string "Resolved" or an object { status: 200 }
else
return await OtherPromise();
}
You could use the $q.defer() like this:
this.update = function (data_loaded) {
var deferred = $q.defer();
if (data_loaded) {
deferred.resolve(null); // put something that your callback will know the data is loaded or just put your loaded data here.
} else {
Restangular.all('someBase').customGet('foo/bar').then(function(data) {
// Do something here when update is finished
deferred.resolve(data);
}
}
return deferred.promise;
};
Hope this helps.