Chaining JavaScript Promises that include nested AJAX calls - javascript

I'm looking to replace some of my existing code with JavaScript Promises and I just want to confirm that I'm not doing it wrong (or maybe there are better ways). I'm using the es6-promise library.
What I have are three functions that I've updated to use JavaScript Promises (what was previously a nested, callback-ey mess). The functions are actually supposed to have dual modes i.e. I can use them like regular functions and have them return a result, or I could chain them together.
function func_1()
{
return new Promise(function(resolve, reject){
if(condition)
{
resolve('1');
}
else
{
console.log('start');
resolve(ajaxRequest(url));
}
});
}
function func_2(data)
{
return new Promise(function(resolve, reject){
if(condition)
{
resolve('2');
}
else
{
console.log(data.response);
resolve(ajaxRequest(url));
}
});
}
function func_3(data)
{
return new Promise(function(resolve, reject){
if(condition)
{
resolve('3');
}
else
{
console.log(data.response);
resolve(ajaxRequest(url));
}
});
}
func_1().then(func_2).then(func_3).then(function(data){});
The if is to check whether data is cached in localStorage/sessionStorage, and if so the function just returns the result. However, in circumstances where I am unsure if the values have been cached (e.g. first run of the script), then I plan to chain them and then have each subsequent function persist the result from its preceeding one to localStorage/sessionStorage (hence the promise chain on the last line). The final then gives me an opportunity to persist the data from func_3.
Based on the tests I have run, everything seems to be working ok, but I was just wondering if this was the best way of doing this? And how do I handle the AJAX errors that could happen on one or more of the 3 listed functions?
Note: My AjaxRequest function also uses the same Promise mechanism and 'resolves' a full XHR on success, and 'rejects' the same full XHR on failure/error.
EDIT
After a tip from #Bergi, I've updated the code to look like this (and it works just as well):
function func_1()
{
if(condition)
{
return Promise.resolve('1');
}
else
{
console.log('start');
return ajaxRequest(url);
}
}
function func_2(data)
{
if(condition)
{
return Promise.resolve('2');
}
else
{
console.log(data.response);
return ajaxRequest(url);
}
}
function func_3(data)
{
if(condition)
{
return Promise.resolve('3');
}
else
{
console.log(data.response);
return ajaxRequest(url);
}
}
func_1().then(func_2).then(func_3).then(function(data){})['catch'](function(err){console.log(err)});

everything seems to be working ok, but I was just wondering if this was the best way of doing this?
You should never really need to use the Promise constructor except on the lowest level (i.e. when promisifying that ajax request). Your functions should simply be written like this:
function func_1() {
if (condition) {
return Promise.resolve('1');
} else {
console.log('start');
retrun ajaxRequest(url);
}
}
Strictly speaking, the func_2 and func_3 might even do return '2', the then method for which they are used as callbacks can cope with that. Of course it is cleaner to always return a promise object.
And how do I handle the AJAX errors that could happen on one or more of the 3 listed functions?
Pass a second function to then, or use catch. This callback will get called when the promise is rejected, and should handle the exception.

Related

How to use JavaScript Promise with conditional logic and store into local variable before returning?

I would like to involve more logic than just return new Promise(...); from a function. I'm not sure if storing a Promise into a variable is changing the "synchronicity" behind it, as every example I've seen does not do that. I would also like to store a Promise into a variable in general to return at the end, even when I'm not involving conditionals, as it just seems bad for debugging and in general to have all the logic reside in a return statement.
function doSomething(){
var promise_var1;
if (condition){
//Do something async, like an AJAX call to an API
setTimeout(function(){promise_var1 = Promise.resolve('a')}, 3000);
}else{
//Do something synchronous, like setting a default value
promise_var1 = Promise.resolve('b');
}
return promise_var1;
}
doSomething().then(function(value){console.log('c'+value)});
I'm currently getting
doSomething() is undefined
however I've tried various ways so I may be misunderstanding something conceptually.
Edit: "Duplicate" question is asking how to handle conditionals inside a promise. I'm asking how to return promises via variable (to achieve single exit). That may not be possible because JavaScript is weakly typed and Promise variable return vs. Promise return may behave differently, but if there is a solution to do that I would prefer single-exit over multiple returns.
Try writing your promise like this.
function doSomething(condition){
var promise_var1= new Promise(function(resolve, reject) {
if(condition){
//resolve after your async action
setTimeout(resolve('a'), 3000);
}else{
//resolve immediately
resolve('b');
}
});
return promise_var1;
}
To answer your question simply: Yes. You can absolutely store a promise in a variable. This is exactly why we have promises. It allows us to "store" a value that may not exist yet, so we can easily pass it around to other parts of the program.
Ultimately you still need to call a callback to get at the internal value, either with .then or with the overly-sweet sugar of async/await. But until you are ready to define your callback, you can pass that future-value around just like any other value.
However, in your example, I don't think the use of a temporary variable does anything to improve the logic or readability of your code. (And in fact, it doesn't work at all, because your if doesn't add a promise to the returned variable until 3 seconds later, after .then has already been called.) Just make sure that every branch of your conditional returns a promise, and you're good to go.
function doSomething(condition){
if (condition){
//Do something async, like an AJAX call to an API
return new Promise(resolve => {
setTimeout(() => resolve('a'), 3000);
})
}
//Don't actually need an else because if condition returns.
//Do something synchronous, like setting a default value.
return Promise.resolve('b');
}
doSomething(true)
.then(value => console.log('c ' + value));
doSomething(false)
.then(value => console.log('c ' + value));
Why make it complicated?
let doSomething = condition => condition ? somethingAsync() : Promise.resolve('b');
function always returns a Promise you can .then whether it resolves in one turn of the event loop or 3 minutes
Update
here's a version with one return and the branch inside the Promise
let doSomething = condition => {
return new Promise(resolve => {
if (condition) {
resolve(someAsyncThing());
} else {
resolve('b');
}
});
};
I would still argue that it's less readable and simple than the version above, but it's your codebase, YMMV.

Should I return the callback or just run it?

I'm currently writing a couple of functions which work async.just imagine this example
signUpUser(userInfo, callback) //callback(err, token)
createToken(userInfo, callback) // callback(err, token)
now I'm wondering whether to return the callbacks or just call'em inside my functions, I mean for example in the createToken:
createToken(userInfo, callback) {
var token = ...;
callback(err, token); //or
return callback(err, token);
}
cause as long as the callback belongs to another programer and if for what ever reason he decides to return something in his code this simply blocks it.
EDIT:After I posted my question I got the answer that it's not possible to return anything but as I went deeper in my codes and also node js I found the opposite.the code below runs well!!
function foo(a, callback) {
console.log(a);
return callback(a);
}
function bar(a) {
return a;
}
console.log(foo(3, bar));
You've said your functions are asynchronous. That means they can't return the result of calling the callbacks, because they return before the callbacks are ever called. So they should return nothing or some useful context value, but not the result of calling the callback. So of the two choices in your question:
function createToken(userInfo, callback) {
startAsyncProcess(function() {
// It's done, may have failed (err), may have succeeded (token)
callback(err, token);
});
}
Separately and a bit off-topic: Look at using promises rather than manual callback mechanisms (unless of course the manual style is to fit into a large established codebase). E.g.:
function createToken(userInfo) {
return new Promise(function(resolve, reject) {
startAsyncProcess(function() {
if (/*...it succeeded...*/) {
resolve(token);
} else /* ...it failed... */ {
reject(err);
}
});
});
}
(Of course, if startAsyncProcess provides a promise, you'd use that promise.)
Usage:
createToken(/*...info...*/)
.then(function(token) {
// It worked, use the token
})
.catch(function(err) {
// It failed
});
If you returned the callback, so how you will now that the execution of the async class finished?
That's why we send the callback to the asyc work, to call it after finishing its functionality or getting the response from the server.
You can use Promises, and here the MDN documentation.
The idea behind the promise, it you return an object that has methods for each case of your async action like fulfilled, and rejected

Is this a good use case for using Promises?

I'm working with a string of XMLHttpRequests that each depend on the one prior to it. Psuedocode:
xhr1.open('GET', 'http://foo.com');
xhr1.onload = function(e){
xhr2.open('POST', xhr1.response.url)
xhr2.onload = function(e){
xhr3.open('GET', xhr2.response.url2);
xhr3.onload = function(e){
console.log('hooray! you have data from the 3rd URL!');
}
xhr3.send();
}
xhr2.send();
}
xhr1.send();
Is this the kind of situation where using promises would be a good idea to avoid all the callback muck?
Yes. If you return a promise in a then, the next chained then listens for that promise instead of resolving from the original promise. Given that ajaxCall returns a promise, your code will then look like:
ajaxCall(1)
.then(function(result1){
return ajaxCall(2);
})
.then(function(result2){
return ajaxCall(3);
})
.then(function(result3){
// all done
});
// Sample AJAX call
function ajaxCall(){
return new Promise(function(resolve, reject){
// xhr code. call resolve/reject with accordingly
// args passed into resolve/reject will be passed as result in then
});
}
Yes, definitively. Assuming a helper function like those from How do I promisify native XHR?, your code could be transformed into
makeRequest('GET', 'http://foo.com').then(function(response1) {
return makeRequest('POST', response1.url);
}).then(function(response2) {
return makeRequest('GET', response2.url2);
}).then(function(response3) {
console.log('hooray! you have data from the 3rd URL!');
});
Still callbacks of course, but no more nesting required. Also you'd have simple error handling, and the code looks much cleaner (partially contributed to by the non-promise-related fact of abstracting XHR in its own function).

Why is the Promise constructor so verbose?

This is not a "how does it work" but "why is it this way - what am I missing" question:
I think I've gotten the hang of the javascript Promise construct - very clever, simple and nice. I love it. But I do wonder - and I'm sure someone can give me a good answer - why is the Promise constructor different from the .then() method?
some_promise.then(
function(){
if (some_condition) {
return 'It works!';
} else {
throw Error('Noooooo!');
}
).then(
function(message){
console.log('I was told: '+message);
}
).catch(
function(err) {
console.log('Dang, it broke!');
}
)
works like a charm, but in order to get the original promise to work, you HAVE to do:
var some_promise = new Promise(function(resolve, reject){
if (some_condition) {
resolve('It works!');
} else {
reject(Error('Noooooo!'));
}
});
I had a hard time wrapping my head around promises at the beginning. The concept was simple enough, but the examples (like above) confused me greatly, and I figured out it was this difference that got me - it seems inconsistent to me. Is there any reason why the standard isn't more along the lines of:
//Modification to the standard
Promise.to = function(action){
return new Promise(function(resolve, reject){
resolve(true);
}).then(action);
}
//Would result in a new Promise being constructed / used like this:
var some_promise = Promise.to(function(){
if (some_condition) {
return 'It works!';
} else {
throw Error('Noooooo!');
}
}).then(
function(message){
console.log('I was told: '+message);
}
).catch(
function(err) {
console.log('Dang, it broke!');
}
)
I don't do this, because I think it's a bad idea to modify the base "classes" in javascript, but the example above seems to be working.
What would be lost if the standard worked this way? I guess something, but what am I not getting? It seems to be simpler to grasp, when getting aquainted with this concept and it's less verbose.
The entire purpose of the resolve and reject callbacks is that they can be called at any time, even after the construction function has finished. This makes is useful for asynchronous operations which can be started at construction but not completed before the constructor completes. As your examples only deal with the synchronous case, this advantage is lost. (...actually, it's more than an advantage... it's the entire reason for the existence of Promises).
You can also return promises from your continuations, such that the subsequent continuation (or .then clause) will only run when the previously returned Promise has completed. All good, but you'd be left with the problem of how to make an async promise in the first place without the original interface.
The point of the promise constructor is indeed to allow asynchronous promise completion.
However, if you are not doing that, then what you describe is present in many promise libraries. For example, in bluebird it is Promise.try:
var some_promise = Promise.try(function(){
if (some_condition) {
return 'It works!';
} else {
throw Error('Noooooo!');
}
}).then(
function(message){
console.log('I was told: '+message);
}
).catch(
function(err) {
console.log('Dang, it broke!');
}
)
Also, if you want to use then, you can just start off with an already resolved promise by using Promise.resolve:
Promise.resolve(undefined).then(
function(){
if (some_condition) {
return 'It works!';
} else {
throw Error('Noooooo!');
}
).then(
function(message){
console.log('I was told: '+message);
}
).catch(
function(err) {
console.log('Dang, it broke!');
}
)
But, to reiterate, this is only relevant if you do not actually need the asynchronous nature of the promise constructor - like in the examples you gave above. It is not rationale for making the Promise constructor any different from what it is, because it has a well defined purpose and need.

Using Javascript native promises, resolve a promise after thenables attached

Is there a way using javascript native promises (docs) to create a promise and attach thenables, without knowing at constructor time how it will resolve?
var foo = new Promise(function(resolve, reject) {
// I don't know how this will resolve yet as some other object will resolve it
});
foo.then(function(val) {
console.log("first " + val);
});
foo.resolve("bar");
foo.then(function(val) {
console.log("second " + val);
});
// result
// first bar
// second bar
Simply save them inside of a closure.
var makePromise = function () {
var resolvePromise = null,
rejectPromise = null,
promise = new Promise(function (resolve, reject) {
resolvePromise = resolve;
rejectPromise = reject;
});
return { promise : promise, resolve : resolvePromise, reject : rejectPromise };
};
var deferredSomething = function () {
var deferredThing = makePromise();
waitAWhile()
.then(doStuff)
.then(function (result) {
if (result.isGood) {
deferredThing.resolve(result.data);
} else {
deferredThing.reject(result.error);
}
});
return deferredThing.promise;
};
This is actually the majority of the difference between the "deferred" concept and the "promise" concept; one more level, on top, that has the actual remote-controls that you can give to someone else, while you hand the .then|.success|.done|etc... to your consumers.
Once you bring those functions out into your upstream process, you can happily lazy-load whatever you'd like, using the "thenable" which you'll return, and then succeed or fail your chain (or leave it hanging) at will...
UPDATE
Seeing as this is probably going to continue to be the chosen answer, and continue to be voted down, as the solution to the exact problem he was having (ie: retrofitting code which was not made with ES6 promises in mind), I figure I'll add a more detailed example of exactly why using this antipattern selectively can be better than naught:
MongoClient.connect("mongodb://localhost:21017/mydb", (err, db) => {
db.collection("mycollection", (err, collection) => {
collection.find().toArray((err, results) => {
doStuff(results);
});
});
});
If I were to write a library, here, hoping to reach the point where I could write:
let dbConnected = MongoClient.connect(dbURL);
dbConnected
.then(db => db.collection(myCollection))
.then(collection => collection.find(query))
.then(stream => doStuff(stream));
...or alternatively:
composeAsync(
(stream) => doStuff(stream),
(collection) => collection.find(query),
(db) => dbCollection(myCollection)
)(dbConnected);
...for ease of use within the library, does it make sense to wrap every single function-body within an instantiated promise
// find = curry(query, collection)
return new Promise(resolve, reject) {
/* whole function body, here /
/ do lots of stuff which is irrelevant to the resolution of mongo.db.collection.find, but is relevant to its calling */
collection.find(query).toArray( /node-callback/(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
};
...or in looking at the pattern of really only requiring the node-specific callback to be resolved, does it make more sense to have some form of promise-resolver, to save having to write out / copy-paste a half-dozen purely-redundant lines which should be completely DRYed up?
// find = curry(query, collection)
let resolver = new NodeResolver();
collection.find(query).toArray(promise.resolve);
return resolver.promise;
Yes, that is an anti-pattern... ...yet, an antipattern which requires fewer keystrokes, restores the natural flow of the promise-chain, fixes a problem with Node's callback-only API, reduces the potential for errors, et cetera.
Yes, there are already libraries which do this...
...solutions specific to X library or Y...
...or solutions which globally override methods of various modules (scary)
...or solutions which, again, basically force you to pass in all of the details of the call you're making:
wrapNodeMethod(fs, "read", url, config).then(data => { /*...*/ });
But there is no simple solution for the case of inverting all of that pain, without either:
a) wrapping the entire function body in a promise, to feed the async callback a resolver
b) using an antipattern within a library, in order to pass Node callbacks a resolver that the rest of the function-body needs to know precisely nothing about.
Even if data needed to be transformed within the resolver, it would still make more sense to return a transformed set, in a new promise
let resolver = new NodeResolver();
somethingAsync(resolver.resolve);
return resolver.promise.then(transformData).then(logTransform);
...than to wrap the whole body, including transforms, etc, just for closure-scope reference, just for the sake of avoiding an "antipattern" which clearly goes against the grain of what has become a very prominent JS platform/paradigm.
Now, personally, I'd be happier if IO||Node methods returned a promise and/or a stream, as well as taking a callback, as a core part of the platform...
...that's not going to happen...
...but you can't possibly tell me that writing less, and keeping Node modules DRY, while still using ES6 Promises is an "antipattern", without providing me a more-eloquent solution, therefore.
If you can, indeed, provide me something that I can use in any NodeJS callback, which does, indeed, provide a more eloquent solution to this, such that I don't have to wrap every body of every method which contains an async callback in a new constructor, or use clunky dispatcher methods, or hijack entire modules to override their global functionality...
...I'd be more than willing to retract my statement that this particular pattern is still highly-useful, as regards interfacing with APIs which are prone to pyramids o'dewm.
If result of promise depends on other promise, you should just create a promise using then.
What was proposed by #Norguard in direct form, doesn't make much sense (it's even coined as deferred anti-pattern). Below code does exactly same, and no extra promise is needed:
var deferredSomething = function () {
return waitAWhile()
.then(doStuff)
.then(function (result) {
if (result.isGood) {
return result.data;
} else {
throw result.error;
}
});
});
};
And, even if, for whatever reason you would need to create promise upfront, then with constructor pattern it would be cleaner to do it that way:
var deferredSomething = function () {
return new Promise(function (resolve, reject) {
waitAWhile()
.then(doStuff)
.then(function (result) {
if (result.isGood) {
resolve(result.data);
} else {
reject(result.error);
}
});
});
};

Categories

Resources