My Promise going into both then and catch - javascript

I have a set of nested promises shown below. The expected behaviour here is for 1 to be printed, then 2 to be printed and then for the callback to be called under dropbox_functions.moveFolder('test', data.ui). However what is happening is that 1 is printed then 2 is printed and the 2.1 is printed, so the 2 promise is going into the then and the catch. I cant work out why.
dropbox_functions.createBatchFolder(data.ui)
.then(function(response) {
console.log('1')
console.log(response)
dropbox_functions.checkScannerFolderExists('test')
.then(function(response) {
console.log('2')
console.log(response)
dropbox_functions.moveFolder('test', data.ui)
.then(function(response) {
console.log(response)
callback(null, data)
})
.catch(function(error) {
console.log(error);
callback('Data not copied from scanner', data)
});
})
.catch(function(error) {
console.log('2.1')
console.log(response)
dropbox_functions.createDataFolder(data.ui)
.then(function(response) {
console.log(response)
callback('No scanned folder', data)
})
.catch(function(error) {
console.log(error);
callback('Data Folder not created', data)
});
});
// callback(null, data)
})
.catch(function(error) {
console.log('1.2')
console.log(error)
callback('Folder not created', data)
});

There must be an error thrown, after execution of line console.log('2'). If an error thrown within a promise execution, the next immediate catch block will catch the particular error.
console.log('2')
console.log(response) <-- Here
dropbox_functions.moveFolder('test', data.ui) <--- Here
.then(function(response) {
console.log(response)
callback(null, data)
})
.catch(function(error) { <-- or here
console.log(error);
callback('Data not copied from scanner', data)
});

This isn't an answer but an recommendation. The whole point of promises is that you don't have to nest them, but can chain them instead. This is done by simply returning a new promise as return value of the function provided to then. You can then chain another then statement after it that handles the newly resolved promise.
dropbox_functions
.createBatchFolder(data.ui)
.then(function (response) { // response of createBatchFolder
console.log('1');
console.log(response);
return dropbox_functions.checkScannerFolderExists('test');
})
.then(function (response) { // response of checkScannerFolderExists
console.log('2');
console.log(response);
return dropbox_functions.moveFolder('test', data.ui);
})
.then(function (response) { // response of moveFolder
console.log(response);
callback(null, data);
})
.catch(function (error) { // some error occurred
console.error(error);
callback(error, data);
});
If you need to do something for specific errors you can check the name and/or description properties of the error or convert it to a string by calling toString() on it.
Furthermore by calling a callback function yourself you still introduce callback hell into your program. I'd recommend returning a promise instead and work with that. If the code above is the last statement of your function you can simply return a resolved promise from the last then statement.
function yourFunction() {
// ...
return dropbox_functions
.createBatchFolder(data.ui)
// ...
.then(function (response) { // response of moveFolder
console.log(response);
return Promise.resolve([null, data]);
})
.catch(function (error) { // some error occurred
console.error(error);
return Promise.reject([error, data]);
});
}
If it's not the last statement simply save the resulting promise into a variable and return that after you did you other stuff. You could even leave out the catch statement if all you do is forward it into Promise.reject(...) (this is currently not the case since you add data too). This can then be handled by the code calling yourFunction.

Related

Proper mindset for use of promises?

I've only recently looked at promises (JS not being my forte) and I'm not sure what the proper way to do this is. Promises are supposed to prevent right-drifting code but when I end up with somewhat complex logic I end up nested far too deep anyway, so I'm convinced I'm doing it wrong.
If I'm returning both successes and failures as json values, and I want to handle malformed json as well, I immediately think to do something like this:
fetch('json').then(function (result) {
return result.json();
}).catch(function (result) {
console.error("Json parse failed!");
console.error(result.text);
}).then(function (wat) {
// if (!result.ok) { throw...
}).catch(function (wat) {
// Catch http error codes and log the json.errormessage
});
Of course, this won't work. This is stereotypical synchronous code. But it's the first thing that comes to mind. Problems I can see:
How do I get both the response and the json output?
How do I get separate control flow for errors and successes?
How do I catch a json parse error on both types of response?
My best attempt involves nesting to the point where I might as well be using callbacks, and it doesn't work in the end because I still haven't solved any of the above problems:
fetch('json').then(function (response) {
if (!response.ok) {
throw response;
}
}).then(
function (response) {
response.json().then(function (data) {
console.log(data);
});
},
function (response) {
response.json().then(function (data) {
console.error(data.errormessage);
});
}
).catch(function () {
console.error("Json parse failed!");
// Where's my response????
});
What's the "Right" way to do this? (Or at least less wrong)
If you want to call response.json() anyway (for successful and failed response) and want to use the response together will the response data. Use Promise.all:
fetch('json')
.then(response => Promise.all([response, response.json()]))
.then(([response, data]) => {
if (!response.ok) {
console.error(data.errormessage);
} else {
console.log(data);
}
})
.catch(err => {
if (/* if http error */) {
console.error('Http error');
} else if (/* if json parse error */)
console.error('Json parse failed');
} else {
console.error('Unknown error: ' + err);
}
});
You shouldn't use exceptions for control flow in Promises any more than you should when not using Promises. That's why fetch itself doesn't just reject the promise for status codes other than 200.
Here's one suggestion, but the answer will necessarily depend on your specific needs.
fetch('json').then(function (response) {
if (!response.ok) {
response.json().then(function (data) {
console.error(data.errorMessage);
});
return ...;
}
return response.json().catch(function () {
console.error("Json parse failed!");
return ...;
});
}).catch(function (e) {
console.error(e);
return ...;
});

Error : Callback was already called when using pg-promise with async series

I'm having trouble understanding the output printed why executing this code :
1
2
Unhandled rejection Error: Callback was already called.
It seems like both then and catch are executed when the query is successful.
Any idea ?
Cheers
async.series([
function(callback) {
db.none(query)
.then(function () {
return callback(null, true);
})
.catch(function (err) {
return callback(err, null);
});
},
function(callback) {
db.any(query)
.then(function (data) {
console.log('1')
return callback(null, data);
})
.catch(function (err) {
console.log('2')
console.log(err);
return callback(err, null);
});
}
],
function(err, results) {
if (results && !results[1].isEmpty()) {
// do something
}
});
EDIT :
TypeError: results[1].isEmpty is not a function
It seems like the problem come from the rest of the code and was just a simple undefined function error, thanks.
But i still don't understand something : why is this error catched inside the second query instead of outside the async queries ?
I'm the author of pg-promise.
You should never use async library with pg-promise, it goes against the concept of shared/reusable connections.
Implementation with proper use of the same connection, via a task:
db.task(t => {
return t.batch([
t.none(query1),
t.any(query2)
]);
})
.then(data => {
// data[0] = null - result of the first query
// data[1] = [rows...] - result of the second query
callback(null, data); // this will work, but ill-advised
})
.catch(error => {
callback(error, null); // this will work, but ill-advised
});
See also: Chaining Queries.
However, in your case it looks like when you call the successful callback(null, data), it throws an error, which in turn results in it being caught in the following .catch section. To test this, you can change your promise handler like this:
.then(data => {
callback(null, data);
}, error => {
callback(error, null);
});
It should normally throw an error about Promise missing .catch because you threw an error while in .then and there is no corresponding .catch chained below, which you can also check through this code:
.then(data => {
callback(null, data);
}, error => {
callback(error, null);
})
.catch(error => {
// if we are here, it means our callback(null, data) threw an error
});
P.S. You really should learn to use promises properly, and avoid any callbacks altogether. I only provided an example consistent with your own, but in general, converting promises into callbacks is a very bad coding technique.
This is what happens:
callback(null, data) is called within the context of the .then();
async notices that this was the last item of the series, so it calls the final handler (still within the context of the .then());
the final handler throws an error;
because the code runs in the context of .then(), the promise implementation catches the error and calls the .catch();
this calls the callback again;
PoC:
const async = require('async');
async.series([
callback => {
Promise.resolve().then(() => {
callback(null);
}).catch(e => {
callback(e);
});
}
], err => {
throw Error();
})
Have you try to define your function externally:
function onYourFunction() {
console.log('Hi function');
}
and than do:
.then(onYourFunction) //-->(onYourFunction without parentheses )
Unfortunately i don't use pg-promise but i can advise promise
at this point i create all promises that are necessary:
function createPromise(currObj) {
return new Promise(function (resolve, reject) {
currObj.save(function (errSaving, savedObj) {
if(errSaving){
console.log("reject!");
return reject(errSaving, response);
}
console.log('currObj:' + currObj);
return resolve(savedObj);
});
});
}
and then in cascades:
var allPromiseOs = Promise.all(promise1, promise2, promise3);

Javascript http get success function capturing error response

I am making a http request to a url which is returing a 500 error response(This is the expected behavior). But this is error is getting captured in the success function instead of error function.
$http.get("myUrl")
.then(function (response) {
console.log(response);
}
.function (error) {
// Handle error here
});
Please help in understanding this and the correct way to use this.
It should be either:
$http.get("myUrl")
.then(function (response) {
console.log(response);
}
,function (error) {
// Handle error here
});
Or
$http.get("myUrl")
.then(function (response) {
console.log(response);
})
.catch (function(error) {
// Handle error here
});
If this is angulars $http, it's supposed to be something like this:
$http.get("myUrl")
.then(
function (response) {
console.log(response);
},
function (error) {
// Handle error here
}
);
You want two functions as your arguments to then(). The first is your successCallback, the second your errorCallback.
As an alternative you may add an catch() to your promise chain. Which is easier to read and prevents errors like yours.
I had an interceptor in my application which was causing the problem.
All my error responses were intercepted and returned without a status. Below is the code.
return {
'responseError': function(config) {
if(config.status===401){
//Do Something
return config;
}
return config;
}
}
Changing the return statement to return $q.reject(config); started returning the correct status.

Calling another prototype method after promise completed

I have following code in my node.js file;
GameHelperAuth.prototype.GetUserViaApi = Promise.method(function (authCookie, callback) {
// get user from API
});
GameHelperAuth.prototype.GetObjectFromCache = Promise.method(function (authCookie, callback) {
// get user from Cache
});
GameHelperAuth.prototype.GetUser = function (authCookie, callback) {
// check cache
this.GetObjectFromCache()
.then(function (result) {
if (result) {
return callback(null, result);
}
else {
// not found in cache, get it from API
// **NOT WORKING HERE - undefined error**
this.GetUserViaApi(authCookie)
.then(function (apiResult) {
return callback(null, apiResult);
}).catch(function (err) {
throw err;
});
}
})
.catch(function (err) {
throw err;
});
I would like to access my instance method from another instance method once promise is completed. But it looks like it loses it's context and cannot find function anymore. (Please see where I am calling GetUserViaApi method)
Is there any way for me to reach that method without creating new instance of my class?
As far as I can see, the simplest fix here is to just declare var self = this in the first line of .GetUser() and then use self instead of this inside the .then callback.
Alternatively if you're using Node 4+ with ES6 compatibility, use an "arrow function" as the outer .then callback that inherits the lexical this instead of contextual this:
return this.GetObjectFromCache()
.then((result) => {
if (result) {
return callback(null, result);
} else {
// not found in cache, get it from API
return this.GetUserViaApi(authCookie)
.then(function (apiResult) {
return callback(null, apiResult);
}).catch(function (err) {
throw err;
});
}
})
.catch(function (err) {
throw err;
});
NB: note the addition of the return in the first line and in the else clause, necessary to ensure that the function and that branch both correctly return a promise.
FWIW, I also think you can refactor this substantially by eliminating the repeated call to return callback(...) through a chaining .then:
GameHelperAuth.prototype.GetUser = function (authCookie, callback) {
return this.GetObjectFromCache()
.then(result => result || this.GetUserViaApi(authCookie))
.then(result => callback(null, result));
}
I've removed both .catch blocks - doing .catch(function(err) { throw err }) is a no-op - AIUI the throw would make the caller end up in their own .catch block so you might just as well let the entire promise reject anyway.

Return 2 variables using bluebird promises

I am trying to write a promise function using Bluebird library for nodejs. I want to return 2 variables from my function.
I want the first function to return immediately and the second to complete its own promise chain before returning.
function mainfunction() {
return callHelperfunction()
.then(function (data) {
//do something with data
//send 200 Ok to user
})
.then(function (data2) {
//wait for response from startthisfunction here
})
.catch(function (err) {
//handle errors
});
}
function callHelperfunction() {
return anotherHelperFunction()
.then(function (data) {
return data;
return startthisfunction(data)
.then(function () {
//do something more!
})
});
}
Just like regular functions only have one return value, similarly promises only resolve with one value since it's the same analogy.
Just like with regular functions, you can return a composite value from a promise, you can also consume it using .spread for ease if you return an array:
Promise.resolve().then(function(el){
return [Promise.resolve(1), Promise.delay(1000).return(2));
}).spread(function(val1, val2){
// two values can be accessed here
console.log(val1, val2); // 1, 2
});
The only thing that appears to be wrong is the expectation that do something with data; send 200 Ok to user; should be performed in mainfunction(), part way through the promise chain in callHelperfunction().
This can be overcome in a number of ways. Here's a couple :
1. Move do something with data; send 200 Ok to user; into callHelperfunction()
function mainfunction() {
return callHelperfunction())
.catch(function (err) {
//handle errors
});
}
function callHelperfunction() {
return anotherHelperFunction()
.then(function (data1) {
//do something with data
//send 200 Ok to user
return startthisfunction(data1)
.then(function (data2) {
//wait for response from startthisfunction here
//do something more!
});
});
}
2. Dispense with callHelperfunction() altogether and do everything in mainfunction()
function mainfunction() {
return anotherHelperFunction()
.then(function (data1) {
//do something with data1
//send 200 Ok to user
return startthisfunction(data1);
})
.then(function (data2) {
//wait for response from startthisfunction here
})
.catch(function (err) {
//handle errors
});
}

Categories