Passing results via promise chain not accessible? - javascript

According to my code below i am chaining all the data until the end where i wish to render the data to the view however using the .catch i found that summoner is not accessible at the final function.
getSummonerData(req.params.playerName)
.then(function(summoner) {
return getMatchIds(summoner[0].id);
})
.then(function(matchIds) {
return getGameData(matchIds);
})
.then(function(gameData) {
res.render('profile', {player:summoner, games:gameData});
})
.catch(function(e) {
console.log(e);
});

In your code, summoner is only accessible to the then callback containing your call to getMatchIds, not elsewhere. To be accessible later, you'd have to either 1) Return it from that then callback along with the game data, or 2) Nest the then callbacks that need it inside that callback.
The latter is probably the easier one:
getSummonerData(req.params.playerName)
.then(function(summoner) {
return getMatchIds(summoner[0].id)
.then(function(matchIds) {
return getGameData(matchIds);
})
.then(function(gameData) {
res.render('profile', {player:summoner, games:gameData});
});
})
.catch(function(e) {
console.log(e);
});

I am not sure what you mean by "promise function". I am guessing you are not aware that 'then' and 'catch' always return promises. That is why you can chain them. Each 'then' or 'catch' is a method of the promise returned by its predecessor. Those chained promises are later resolved or rejected depending on what happens to their predecessors.
I assume your last function 'res.render(...)' returns the value you want to see. Then the promise 'then(render(...))' will become resolved with the value received from 'res.render(...)'.
So that is what 'catch' will be working with: a resolved promise with the value you want to see. But 'catch' only fires its function with a 'rejected' promise. You need a 'then' instead.

Related

Promise - Call a function in between two promises

I have two requests and some function to be called in between. The flow is that when the first promise is called and finished, no matter what result is (success or failure) some non-promise related function should be called and only after we should call the second promise. So this is how I ended up doing it, which does not look like a good solution.
funtionReturnsPromise()
.then(()=>{})
.catch(()=>{})
.then(()=>{
nonPromiseRelatedFuntion()
})
.then(()=>{
return funtionReturnsPromise2()
})
Since the desired flow is:
Promise > Function > Promise
On which the function is executed no matter the outcome of the first promise, you can simply do something like:
function secondFunction(outcome) {
// do stuff
return funtionReturnsPromise2()
}
functionReturnsPromise().then(secondFunction).catch(secondFunction)
Now, on another topic, I would not call the second function 'unrelated' since it clearly, according to your explanation, needs to be called after the first promise is fulfilled.
Assuming nonPromiseRelatedFuntion is sync and you're not interested in return value of functionReturnsPromise
functionReturnsPromise()
.then(
// no matter what happens, the function is invoked
() => { nonPromiseRelatedFunction() },
error => {
nonPromiseRelatedFunction()
// do error handling from functionReturnsPromise
}
)
.then(() => functionReturnsPromise2() }
.catch(console.error)
If you need a value:
functionReturnsPromise()
.then(
value => {
nonPromiseRelatedFunction()
return functionReturnsPromise2(value)
},
error => {
nonPromiseRelatedFunction()
// do error handling from functionReturnsPromise
}
)
.catch(console.error) // error handling from functionReturnsPromise2
Like the other answers I'm assuming
nonPromiseRelatedFunction is synchronous
you really don't care about any return values
nonPromiseRelatedFunction and funtionReturnsPromise2 should be executed in all circumstances
Having read ALL the comments to the question, I see the above is not an assumption after all
The simplest solution is to get rid of the first .then
funtionReturnsPromise()
.catch(()=>{})
.then(()=>{
nonPromiseRelatedFuntion()
})
.then(()=>{
return funtionReturnsPromise2()
})
Note: such code could be written
funtionReturnsPromise()
.catch(()=>{})
.then(nonPromiseRelatedFuntion)
.then(funtionReturnsPromise2)
sure, the last two functions will receive arguments, but if the code in those functions ignores arguments anyway, then their will be no issue

promise call separate from promise-resolution

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.

Recovering from rejected promises in JS

I'm using native promises (mostly) and attempting to recover from an error and continue executing the promise chain.
Effectively, I'm doing this:
REST query to see if ID exists. Note that this returns a jquery deferred.
.then (success means ID exists, so fail and stop)
(fail means ID does not exist, so continue creating ID)
.then (create the ID record and send to the server)
I return a Promise.resolve() from my rejected function, which should cause the success part of the next .then to execute. It does not. I've tried this on Chrome and Safari.
Note that the first promise is actually a query deferred, but according to this page (http://api.jquery.com/deferred.then/), deferred.then() returns a promise object. So adding an extra .then should covert to native promises.
To make it clearer - here's the pseudocode:
promise = $.ajax(url);
promise = promise.then(); // convert to promise
promise.then(function() { cleanup(); return Promise.reject(); },
function(err) { return Promise.resolve(); });
.then(function() { createIdentityDetails(); });
.then(function() { sendIdentityDetails(); });
Note that I want to FAIL when the ajax returns success, and I want to
continue processing when the ajax call fails.
What happens is that the FAIL functions for all subsequent .then portions execute. That is, my return Promise.resolve() doesn't work - which is (I think) in violation of the spec.
I'd appreciate any feedback on how I can deal with and recover from errors in long promise chains.
Many thanks for any advice you can provide.
p.s. creating and collecting the full identity information is quite time consuming, so I don't want to do it if the ID exists. Hence I want to check first and fail quickly.
p.p.s I really like the way that promises have unwound these deeply nested async callback chains.
Assuming createIdentityDetails() and sendIdentityDetails() to be promise-returning asynchronous functions ...
If what we see in the question is the entirety of the promise chain, then handling the error condition is simple. It's not necessary to convert success to failure or failure to success, or from one type of promise to another.
$.ajax(url).then(function() {
cleanup();
}, function(err) {
createIdentityDetails()
.then(sendIdentityDetails);
});
This will work regardless of the type of promise returned by createIdentityDetails() jQuery or non-jQuery.
If, however, there's more to it, eg a caller function needs to be informed of the outcome, then you need to do more, and it depends on how you want the possible outcomes to be reported.
Report 'ID already exists' as failure and 'new ID created' as success
This is what the question suggests
function foo() {
return $.ajax(url).then(function() {
cleanup();
return $.Deferred().reject('failure: ID already exists');
}, function(err) {
return createIdentityDetails()
.then(sendIdentityDetails)
.then(function() {
return $.when('success: new ID created');
});
});
}
Report both types of outcome as success
This seems more sensible as the handled error will be reported as success. Only unpredicted, unhandled errors will be reported as such.
function foo() {
return $.ajax(url).then(function() {
cleanup();
return 'success: ID already exists';
}, function(err) {
return createIdentityDetails()
.then(sendIdentityDetails)
.then(function() {
return $.when('success: new ID created');
});
});
}
Whichever reporting strategy is adopted, it matters very much what type of promise createIdentityDetails() returns. As the first promise in the chain it determines the behaviour of both its chained .thens.
if createIdentityDetails() returns a native ES6 promise, then no worries, most flavours of promise, even jQuery, will be assimilated.
if createIdentityDetails() returns a jQuery promise, then only jQuery promises will be assimilated. Therefore sendIdentityDetails() must also return a jQuery promise (or an ES6 promise which must be recast into jQuery with $.Deferred(...)), as must the final success converter (as coded above).
You can see the effects of mixing jQuery and ES6 promises in these two ways here. The first alert is generated by the second block of code, and is not what is expected. The second alert is generated by the first block and correctly gives the result 98 + 1 + 1 = 100.
promise = promise.then(); // convert to promise
Huh? A promise returned by $.ajax is already a promise.
promise.then(function() { cleanup(); return Promise.reject(); },
function(err) { return Promise.resolve(); });
The problem with this is that jQuery is not Promises/A+ compatible, and fails to adopt promises/thenable from other implementations than its own. You would have to use $.Deferred here to make this work, like
promise.then(function() { cleanup(); return $.Deferred().reject(); },
function() { return $.when(); }); // or return $.Deferred().resolve();
That is, my return Promise.resolve() doesn't work - which is (I think) in violation of the spec.
Indeed it is. However, jQuery is known for this, and they won't fix it until v3.0.
To get the native Promise library you want to use working, you will need to avoid jQuery's then. This can easily be done:
var $promise = $.ajax(url);
var promise = Promise.resolve($promise); // convert to proper promise
promise.then(function() {
cleanup();
throw undefined;
}, function(err) {
return undefined;
})
.then(createIdentityDetails)
.then(sendIdentityDetails);
It seems that JQuery promises do not permit you to change a failure to a success. If, however, you use native promises, you can.
For example:
Promise.resolve()
.then(function() {console.log("First success"); return Promise.reject(); },
function() { console.log("First fail"); return Promise.resolve(); })
.then(function() {console.log("Second success"); return Promise.reject(); },
function() { console.log("Second fail"); return Promise.resolve(); })
.then(function() {console.log("Third success"); return Promise.reject(); },
function() { console.log("Third fail"); return Promise.resolve(); })
Here I return a reject from the first success handler. In the second failure handler I return a resolve. This all works as expected. The output is (Chrome):
First success
Second fail
Third success
It turns out the proper way to deal with jQuery deferreds and promises is to cast them:
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
(from http://www.html5rocks.com/en/tutorials/es6/promises/).
This works nicely, so if you change the initial line above to:
Promise.resolve($.ajax("this will fail"))
...
you correctly get:
First fail
Second success
Third fail
Bottom line... cast deferred to promise asap, then everything seems to work right.
Hopefully this will clear things up a bit, you had a couple of stray ; and you're doing things you don't really need to do in the then functions
firstly, I'm sure you DO NOT want the
promise = promise.then();
line, the code would look like this
promise = $.ajax(url);
promise.then(function() {
cleanup();
throw 'success is an error'; // this is equivalent to return Promise.reject('success is an error');
}, function(err) {
return 'failure is good'; // returning here means you've nullified the rejection
}) // remove the ; you had on this line
.then(function() { createIdentityDetails(); }) // remove the ; on this line
.then(function() { sendIdentityDetails(); }) // remove the ; on this line
.catch(function(err) { }); // you want to catch the error thrown by success

Promise chaining and error handling

I'm trying to understand chaining and error handing with promises. Here I have some promise chained.
return ad_fetcher.getAds(live_rail_url, ad_time, req.sessionID)
.spread(generator.playlist_manipulate) // returns Promise.resolve([data, anotherData])
.then(client.incrAsync(config.channel_name + ":ad_hits", "vdvd")) // FOCUS HERE
.then(function() {
console.log("AD FETCHED AND PLAYLIST GENERATED.");
res.send(generator.generate_regular(config.default_bitrate));
})
.catch(function(err) {
console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
console.log("!!! AD FETCHER - THERE WAS AN ERROR:!!!!!!!!!!!");
client.sadd(config.channel_name + ":ad_errors", err);
client.incr(config.channel_name + ":ad_errors:count");
console.log(err);
console.log('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!');
res.send(generator.generate_regular(config.default_bitrate));
});
Now here at the line client.incrAsync(config.channel_name + ":ad_hits", "vdvd") I intendedly write wrong syntax to see if error is caught by .catch. But when I run this, I get this:
Unhandled rejection Error: ERR wrong number of arguments for 'incr'
command
But when I change usage of that promise to this:
.
.
.then(function() {
return client.incrAsync(config.channel_name + ":ad_hits", "vdvd");
})
.
.
Error is caught pretty well. It's not "unhandled" anymore.
I don't understand this behavior. Doesn't incrAsync return a promise so it's errors should be caught by the .catch at the end of the chain?
Note: I promisified redis client, no doubt with that.
Thanks!
When you chain promises, you invoke the next function in the chain with the result of the previous function.
However, you're invoking your function that returns a promise directly. So unless invoking that function returns a function that returns a Promise, you're not correctly chaining.
So either of these would work:
.spread(generator.playlist_manipulate) // returns Promise.resolve([data, anotherData])
.then(client.incrAsync) // this function will receive [data, anotherData]
Or, as you used in your question, an anonymous function:
.spread(generator.playlist_manipulate) // returns Promise.resolve([data, anotherData])
.then(function() { // this function receives [data, anotherData] but throws it away
// this Promise is "subsumed" by the Promise chain. The outer Promise BECOMES this Promise
return client.incrAsync(config.channel_name + ":ad_hits", "vdvd");
})
Because otherwise, what you've written is basically this:
.then(function)
.then(Promise)
.then(function)
But you need to pass functions to .then, not Promises, if you want them to be handled by your .catch block at the end.

Is there a good way of short circuiting Javascript promises?

I'm a bit of a novice with promises/Deferreds. Is there a good pattern to handle the case where one might want to short circuit a chain of promises, for both success and error cases? In the error situation, I know you can chain a .then(null, function(error) {}) to the end and catch an error from any of the previous thens, but what if you want to handle an error in a more custom way and terminate? Would you specify a 'type' of error in an earlier error handler and return it via a new promise, to be handled or skipped in the final error handler? And what about a success case, where you want to terminate earlier in the chain (only conditionally firing off any later then's)?
Typically, the promise chain starts with a call to some asynchronous function as such:
var promise = callAsync();
If you are chaining a second async call, you probably do something like this:
var promise = callAsync()
.then(function(){
return callOtherAsync();
})
.then(function(){
return callSuccessAsync();
}, function(){
return callFailAsync();
});
As a result of chaining, promise now contains the final promise which completes when callFinalAsync()'s promise completes. There is no way to short circuit the final promise when using this pattern - you can return a failed promise along the way (for instance, rather than returning the result of callOtherAsync) but that requires the failed promise to progress through the chain (thus causing callFailAsync to be called).
You can always fulfill or reject the promise from within the callbacks as such
var promise = callAsync()
.then(function(){
if(fail){
promise.reject();
//no way to halt progression
}else{
return callOtherAsync();
}
})
.then(function(){
return callSuccessAsync();
}, function(){
return callFailAsync();
});
however, this will not prevent calls to callFailAsync(). Some Promise/A implementations expose a stop method for just this purpose. With stop, you could do this:
var promise = callAsync();
.then(function(){
if(fail){
this.stop();
promise.reject();
}else{
return callOtherAsync();
}
})
.then(function(){
return callSuccessAsync();
}, function(){
return callFailAsync();
});
Which depends on having access to the intermediate promise with this. Some Promise implementations forbid that (forcing this to be window/null/etc), but you can deal with that with a closure.
TL;DR: Promise/A spec doesn't provide a chain short circuit function, but it's not hard to add one.
not sure about jQuery but at least in any Promises/A+ you can just throw:
.then(function() {
if (skip) {
throw new Error("skipping");
}
})
//Chain of thens
.then(...)
.then(...)
.then(...)
.then(...)
.catch(function(){
//skipped here
});
I assume your use case looks like:
promise
.then(a)
.then(b); // We want to have an option to break here
.then(c)
.done(d)
Logical way to handle this is:
promise
.then(a)
.then(function (result) {
if (something) throw new Error("Do not proceed!");
return b(result).then(c).then(d);
}).done();
If you don't like nesting, you may compose b(result).then(c).then(d) as outer function.
I had this exact problem in my application, and achieved short-circuit/cancellation through use of a simple cancellation token object that can be checked for in a Promise's exception/rejection handler callback. Maybe not the most elegant solution, but seems to work well enough without the need for additional libraries or alternate/non-standard Promise implementations
const cancellationToken = {};
somePromiseReturningMethod(...)
.then(doSomething)
.then(doSomethingElse)
.catch(err => {
if (err === cancellationToken)
{
// handle cancellation here and return
}
// handle "regular" errors here (show/log a message, etc)
});
function doSomething(dataFromPromise)
{
// check for whatever condition should result in cancellation/short-circuit
if (...)
{
return Promise.reject(cancellationToken);
}
// carry on as normal...
}

Categories

Resources