promise call separate from promise-resolution - javascript

I'm not so familiar with promises.
I would like hide promise-implementation from promise-call.
Example:
function findFriends(req, res) {
const promiseFriend = MyFriendes.find({}).exec(); //call promise
if(friends.length===0){
logger.warn('No friendsavailible');
}else if(friends === undefined){
res.status(500).json({
error: 'INTERNAL ERROR'
});
}else{
res.status(200).json({
friends: friends
});
}
}
and I will resolve my promise within same file but not
at same function, where I call this promise.
promiseFriend
.then(function(friends){
return friends;
})
.catch(function(err){
logger.error({error:err});
});
Now, I get, that "promiseFriend" is undefined.
How can I separate promise-call from promise-resolution?

If you want to define a promise in a function and use it somewhere else then first of all you need to return the promise from that function, which you're not doing in your code. Then you need to actually call that function which you are also not doing. And finally you need to use a then callback on the returned value, which you are not doing in this case as well.
There is no point in saving the promise in a local variable promiseFriend that is scoped to this function. There is also no point to return a value in your then callback: .then(function (friends) { return friends; }) - I have no idea what have tried to do here.
I suppose that findFriends is supposed to be a route handler for Express. If so then make sure that you send a response in every case (which you don't do for friends.length===0). Also, you need to actually add a then handler to the saved promise if you want to act when it's resolved. Right now you don't even have friends defined in your function. Also add a catch handlers and also send a response for that case.
Then you might return the promise from your function but not if it is a route handler, it doesn't make sense. You can return a promise from a function:
function x() {
return MyFriendes.find({}).exec();
}
and then use it:
x().then(friends => ...).catch(error => ...);
but you cannot use return values if you don't return it, you can't use undefined variables as if they were defined, and you actually need to consider who is your return value returned to.
I suggest that you learn how Node actually works because it seems that you have copied and pasted some random code, joined it together and expect that it does what you want without actually trying to understand it.
To get a better understanding on the asynchronous nature of Node that is giving this execution order here, see those answers:
A detailed explanation on how to use callbacks and promises
Explanation on how to use promises in complex request handlers
An explanation of what a promise really is, on the example of AJAX requests
An explanation of callbacks, promises and how to access data returned asynchronously
Don't try to write Node programs before you understand the concept of function calls, return values, callbacks and in this case promises.

Related

Reusing an existing Promise object if exists

I have a Dialog object that will show, well, dialogs. There are many entry points to show dialogs, e.g. yesNo(), message(), confirm(), etc. However, all these methods basically call the same other method, called showSimpleDialog(title, message, buttons).
I'd like all these methods (showSimpleDialog too) to return a promise, but there's a snag:
yesNo() {
return new Promise((resolve, reject) => {
axios
.get(......)
.then(this.showSimpleDialog(...));
}
}
As you can see, I am prevented in the above example from either returning the promise that showSimpleDialog would make or by passing the instanced Promise to showSimpleDialog.
The former is impossible because we're already in a different Promise by the time we have access to it. The latter because the Promise object itself is not yet available within the constructor. Well, technically, in this particular case it is (exactly because we're already in a different Promise), but some of my entry functions are synchronous, some asynchronous and I simply can't use the same code pattern to achieve the same effect in both cases.
I investigated the thing and I found this, but the author suggests the approach is flawed and archaic to begin with.
So, what would be the correct approach to return a functioning Promise from ALL entry points while the entry points would still be free to reusing each other's Promises?
If I understand correctly, this.showSimpleDialog(...) also returns a Promise, right?
If you want yesNo() to return the Promise retunred by this.showSimpleDialog(...)
yesNo() {
return axios
.get(......)
.then(()=>{
return this.showSimpleDialog(...);
});
}
That being said, consider using async/await, especially when dealing with multiple sequential promises, if possible.
Your code is calling this.showSimpleDialog immediately (synchronously) without waiting for any promise to resolve (the axios one). This is because the code doesn't pass a function to the then method, but instead executes this.showSimpleDialog. This execution returns a promise (presumably), but then expects a function as argument, not a promise.
So you need to make sure to pass a callback to then, and let that callback return a promise. This way promises will be chained:
.then(() => this.showSimpleDialog(...));
It is also important to make that callback an arrow function, since you'll be referencing this, which is intended to be the this on which yesNo is called.

Should I have been returning a promise all along?

I have been calling a method MediaNext() from several points in my PlayersController code to chain the next Media playback if any.
This MediaNext() method returns a Media object from some of the various playlists I have from memory. So it tries to encapsulate the notion of getting the next Media object if any left.
However, recently I revised MediaNext() that for some new condition X I now have to load external data in order to return this alternative Media object.
MediaNext():any { // any could be typed like Media_I
if(cond1) {
...
return Media1
}
if(cond2) {
...
return Media1
}
//
if(! AutoPlayNext) return null;
//
if(NoMoreMedia) return null;
//
If (condX) {
// in order to wait for the Load() method I am subscribing to its promise
this.Service1.Load(Media).then(() => {
...processing...
return Media
})
??? what do I return here, it does not make sense to return null, since potentially a Media is on its way ???
}
return null // safe catch. nomore media should not happen.
}
The questions I have
By subscribing to the promise of the .Load(), the execution from what I gather in JS should not be blocked waiting for the media data to return. Instead the execution needs to flow and the call to MediaNext() has to return. However, what could I be returning where indicated in the code above "???".
Should MediaNext() have been coded to return a promise for all conditions?
I mostly do guess work when I code promises and observables as they remain a beast to understand and master so feel free to advise alternatives.
Thank you for your help.
If you deal with .Load asynchronously and immediately return, there's no good reason for that function to return anything. The operation is still happening in the backend.
However, if you want the caller to get more information about if the operation was successful or not, you should return a promise.
If you want to return a promise, you might find it much easier to write this as an async function, and use await this.Service1.Load(). instead of then().
You method is now asynchronous and you are using a Promise. You then need to return a promise which your consumers can wait on to get a result. Either:
1) Return the promise
If (condX) {
return this.Service1.Load(Media);
}
2) Set your method as async and await the promise used internally
async MediaNext():any { // any could be typed like Media_I
// ...
If (condX) {
return await this.Service1.Load(Media);
}
// ...

Correct way to return a promise from firebase's onCall function?

According to the docs I've read, an onCall function should return a promise. Something like this:
...onCall((data, context) => {
return db.collection('game').doc(game_id).delete();
});
...
This simply takes the promise associated with the delete function and returns it to the caller.
However, if I want to take an additional step after delete, then there are multiple return paths and I'm not clear on what to return:
...onCall((data, context) => {
return db.collection('game').doc(game_id).delete()
.then(){
return db.collection('user').doc(user_id).delete()
}
//.catch(){ return ??? }
});
...
I've seen code similar to what I've typed above, but it isn't clear to me that this is correct.
First, is it saying that by returning the game deletion promise, if there is an error, it will be returned to the user and if it is successful, then the user deletion promise will be returned to the user? What if there is a user deletion exception?
Do I have to create my own Promise object and call resolve or reject myself?
It's fine the way it is. You almost never need to create a new Promise. That's for cases where you call a function that doesn't return a promise, and provides a callback instead.
There are a lot of resources out there on learning to deal with promises. It doesn't really matter if the code is running in Cloud Functions or not - JavaScript promises work the same in any context. A callable function just needs that promise to know when the work is complete, and what to return to the caller.
...onCall((data, context) => {
return db.collection('game').doc(game_id).delete()
.then(){
return db.collection('user').doc(user_id).delete()
}
//.catch(){ return ??? } });
I've seen code similar to what I've typed above, but it isn't clear to
me that this is correct. First, is it saying that by returning the
game deletion promise, if there is an error, it will be returned to
the user and if it is successful, then the user deletion promise will
be returned to the user? What if there is a user deletion exception?
What you have in this code is Promise chaining, with the then() method. Since a call to promise.then() returns a Promise, the first return does return a promise and therefore it is fine for the Callable Cloud Function.
However, to answer to the end of your question, you should note that:
If there is an error in the game deletion, the CF will stop and neither the game nor the user will be deleted
If there is an error in the user deletion, the CF will stop and the game will be deleted (successfully deleted before the error) but not the user.
If you want to be sure both are deleted in one atomic operation (both are deleted or none is deleted), you should use a batched write as follows:
...onCall((data, context) => {
let batch = db.batch();
const gRef = db.collection('game').doc(game_id);
batch.delete(gRef);
const uRef = db.collection('user').doc(user_id);
batch.delete(uRef);
return batch.commit()
});

Break out of a Promise "then" chain with errorCallback

-- EDIT --
I encountered a weird thing recently about promises, but I guess it's maybe because it's against the philosophy of promises.
Considering the following code :
// Assuming Auth is just a simple lib doing http requests with promises
Auth.signup()
.then(succCall, errCall)
.then(loginSucc, loginErr)
// My callbacks here
function succCall (){
// OK, send second promise
console.log('succCall');
return Auth.login();
}
function errCall(){
// I do some things here and now
// I want to break out from here
console.log('errCall');
}
function loginSucc(){
// This is the callback of the login method when it went OK
// I want to enter here ONLY if with go through the succCall
console.log('loginSucc');
}
function loginErr(){
// This is the callback of the login method when it went not ok
// I want to enter here ONLY if with go through the succCall
console.log('loginErr');
}
Here if something goes wrong in Auth.signup(), this is what show :
errCall, loginSucc
if i do a $q.reject() in the errCall this is what happens :
errCall, loginErr
and this is what i want :
errCall... finish, stop here
Now, the problem is, it goes in errCall when signup goes wrong, that's good, but then it enters loginSucc...
I want to break out of the then chain when any errorCallback (which is errCall or loginErr here) is encountered.
-- EDIT --
I think i was misunderstood by some mean, i want to totally break the chain without check in any other "then" if something went wrong.
As if i was saying : if first then is wrong stop here, if first then ok continue, if second "then" ok continue, if third "then" wrong, stop
// Just like if i did the following but by chainning "then" methods
// My callbacks here
function succCall (){
// OK, send second promise
return Auth.login().then(loginSucc, loginErr);
}
My point is, i don't want only one error handler if i have many "then" chained
What is effectively happening is this:
try {
try {
var a = succCall();
} catch(e1) {
a = errCall(e1);
}
var b = loginSucc(a);
} catch(e2) {
b = loginErr(e2);
}
You can break out of the chain by calling
return $q.reject('Reason Err was called');
in your errCall() function.
EDIT:
As OP remarked by calling $q.reject the code will enter the loginErr function.
Alternatively you can modify your code like this:
Auth.signup()
.then(function(a) {
succCall()
return loginSucc(a).then(null, loginErr);
}, errCall)
You can read more in these two SO question:
Break promise chain
Break Out of then promises in
Angularjs
This also is a helpful read : Flattening Promise Chains
errCall function needs tor return a promise, and that promise needs to be rejected for loginErr to be fired.
function errCall(){
// i do some things here and now
return $q(function(resolve, reject) {
// auto reject
reject();
});
}
Alternatively try .catch:
Auth.signup()
.then(succCall)
.then(loginSucc)
.catch(function(err){
// caught error, problem is you won't know which function errored out, so you'll need to look at the error response
});
Just don't pass any errCall or loginErr to then and use catch() in the end of the promise chain and it will be interrupted on first error, which will be passed to catch(). If you want explicitly process error of Auth.signup() then your errCall should look like this:
function (err) {
if(isFatal(err)) {
return Promise.reject(new Error('Fatal!')); //`catch()` handler will be called with `new Error('Fatal!')`
} else {
return 'something'; //next `then()` handler will be called with 'something'
}
}
Your best option is to return a promise that is never resolved from errCall():
function errCall() {
console.log('errCall');
return $q.defer().promise;
}
But you are right, what you are trying to do is "against the philosophy of promises".
Why you shouldn't do it
It is of course a problem that your loginSucc function is called when an error occurs during evaluation of the first promise. However, that can be fixed by rejecting once again in errCall as others have already pointed out. But you should not try to avoid the execution of loginErr, otherwise something is conceptually wrong with your code.
The evaluation of the expression
Auth.signup().then(succCall, errCall)
yields another promise. The contract is that a promise has a then() method taking two callbacks as parameters, one for success and one for failure. The contract is also that the success callback is called when the promise is evaluated successfully and that the error/failure callback is called in all other cases. If you intend to never call either of those, don't use a promise!

Returning angular promise from a function that resolves the promise very quickly

I am writing an asynchronous javascript function that will be called by consumers to get certain data. Following is the simple implementation that I wrote initially (error handing and other stuff removed for clarity).
function getData(callback){
if (data is available as a JavaScript object){
callback(data);
}else{
getAsyncData(function(data){
//some transformations on data
callback(data);
});
}
}
What is important to note is that getData can return data quickly if data is already available as a JavaScript object.
I want to replace this implementation with the one that returns a promise object to the caller. This fiddle shows sample implementation - http://fiddle.jshell.net/ZjUg3/44/
The question - Since getData can return quickly, can there be a possiblity where getData is resolving the promise even before caller has established handler chain using then method? Just to simulate this, in the fiddle if i call then method inside setTimeout function (with zero delay), callback doesn't get called. If i call the then method outside of the setTimeout function, callback gets called. I am not sure if this is even a valid concern or valid usecase. I am quite new to angularjs development and would appreciate your views :)
If you want getData() to return a $q promise instead of using a callback, I'd do the following refactor using $q.when() and usual $q.resolve():
function getData()
{
if (data is available as a JavaScript object) {
return $q.when(data); // resolves immediately
} else {
var q = $q.defer();
getAsyncData(function(data){
//some transformations on data
q.resolve(data);
});
return q.promise;
}
}
No, a significant and important part of being a promise is that it doesn't matter when you attach the handler. Even if you create a promise now and resolve it immediately, then keep your computer running for the next 50 years, then attach a handler it will still fire.
All of this does assume that there isn't a bug/corner case in angularjs's promise implementation. If it doesn't work, it's a bug though.
If you ever need to know anything about how promises work, you can always refer to the Promises/A+ spec which angular adheers to. As a spec, it's one of the simplest and easiest to understand that I've come across (although I should mention that I've been involved in the spec for quite a while now).

Categories

Resources