Stopping the promise chain midway - node.js

I am trying to stop a promise chain midway (After a catch). So after an error occurred in the first promise the catch will catch it but I don't want the chain to continue. I'm using bluebird. How would I do this?
getRedirectedURL(url).then(function(url) {
console.log(1);
url = domainCleanse(url);
sql = mysql.format(select, url);
return [ url, mysqlQuery(sql) ];
}).catch(function(error) {
console.log(2);
console.error(error);
socket.emit('error:unreachable', url + ' was unreachable');
}).spread(function(url, rows) {
console.log(3);
if(_.isEmpty(rows[0])) {
socketList.push({
url: url,
ttl: _.now(),
socket: socket,
added: false
});
} else {
socket.emit('done', mapResults(rows[0]));
}
}).catch(function(error) {
console.log(4);
console.error(error);
socket.emit('error', 'We could not reach ' + url + ' at this time.');
});

Generalizing your example, it looks like this:
promiseToFoo()
.then(promiseToBar)
.catch(failedToFooOrBar)
.then(promiseToFrob)
.catch(failedToFrob)
Along the happy path you are promising to Foo, then to Bar, then to Frob. Based on your description, you want to handle errors Fooing or Barring separately from errors Frobbing. So a simple solution is bury the error handling for Frob into that promise. So instead of chaining a promise to Frob, you're chaining a promise to Frob and handle errors in Frobbing. Something like this:
promiseToFoo()
.then(promiseToBar)
.catch(function (error) {
failedToFooOrBar(error);
return Promise.reject(error);
})
.then(function (x) {
return promiseToFrob(x).catch(failedToFrob);
});
A key to this is to make sure that your on-reject handler in the first catch ends up leaving the chain in a rejected state when it leaves. This is handled in the sample above by returning a rejected Promise from the handler. You can also handle it by throwing an Error from the handler. If you don't do one of these things, then the promise will be in a fulfilled state when the handler finishes and the on-fulfill handler provided by the subsequent then call will be invoked.

Related

Node.js concurrent API GETs, return as soon as one responses

I have a list of APIs I want to call GET simultaneously on all of them and return as soon as one API finishes the request with a response code of 200.
I tried using a for-loop and break, but that doesn't seem to work. It would always use the first API
import axios from 'axios';
const listOfApi = ['https://example.com/api/instanceOne', 'https://example.com/api/instanceTwo'];
let response;
for (const api of listOfApi) {
try {
response = await axios.get(api, {
data: {
url: 'https://example.com/',
},
});
break;
} catch (error) {
console.error(`Error occurred: ${error.message}`);
}
}
You can use Promise.race() to see which of an array of promises finishes first while running all the requests in parallel in flight at the same time:
import axios from 'axios';
const listOfApi = ['https://example.com/api/instanceOne', 'https://example.com/api/instanceTwo'];
Promise.any(listOfApi.map(api => {
return axios.get(api, {data: {url: 'https://example.com/'}}).then(response => {
// skip any responses without a status of 200
if (response.status !== 200) {
throw new Error(`Response status ${response.status}`, {cause: response});
}
return response;
});
})).then(result => {
// first result available here
console.log(result);
}).catch(err => {
console.log(err);
});
Note, this uses Promise.any() which finds the first promise that resolves successfully (skipping promises that reject). You can also use Promise.race() if you want the first promise that resolves or rejects.
I think jfriend00's answer is good, but I want to expand on it a bit and show how it would look with async/await, because that's what you are already using.
As mentioned, you can use Promise.any (or Promise.race). Both take an array of promises as argument. Promise.any will yield the result of the first promise that resolves successfully, while Promise.race will simply wait for the first promise that finishes (regardless of whether it was fulfilled or rejected) and yield its result.
To keep your code in the style of async/await as it originally was, you can map the array using an async callback function, which will effectively return a promise. This way, you don't have to "branch off into .then territory" and can keep the code more readable and easier to expand with conditions, etc.
This way, the code can look as follows:
import axios from 'axios';
const listOfApi = ['https://example.com/api/instanceOne', 'https://example.com/api/instanceTwo'];
try {
const firstResponse = await Promise.any(listOfApi.map(async api => {
const response = await axios.get(api, {
data: {
url: 'https://example.com/',
},
});
if (response.status !== 200) {
throw new Error(`Response status ${response.status}`, {cause: response});
}
return response;
}));
// DO SOMETHING WITH firstResponse HERE
} catch (error) {
console.error('Error occured:', error);
}
Side note: I changed your console.error slightly. Logging only error.message is a common mistake that hinders you from effective debugging later on, because it will lack a lot of important information because it prints only the message and not the error stack, the error name or any additional properties the error may have. Using .stack and not .message will already be better as it includes name and stack then, but what's best is to supply the error as separate argument to console.error so that inspect gets called on it and it can print the whole error object, with stack and any additional properties you may be interested in. This is very valuable when you encounter an error in production that is not so easy to reproduce.

How to handle UnhandledPromiseRejectionWarning

This bit of code, after connecting, does some stuff
controller.connect((response)=>{ does some stuff })
Down deep in the guts of the connect method this async function gets called, which returns a promise by way of the callback
async function ServerSend(endpoint,params,callback) {
const response = axios.get(host+endpoint, {params})
callback(response);
}
If the server is not available it correctly throws: UnhandledPromiseRejectionWarning: Error: connect ECONNREFUSED 127.0.0.1:8088
What is the correct way to handle this exception? I could possibly add a catch in the async method and rewrite all the call backs to return an err. But I'd rather catch it at the caller. I have not been able to get either method to work.
axios.get(host+endpoint, {params}) // this is a promise
so if it resolves it will be ok, but if it rejects (and yuou dont have any try .. catch, any .catch attached - it will throw error that exception is unhandled.
Why way would be to:
async function ServerSend(endpoint,params,callback) {
try {
const response = await axios.get(host+endpoint, {params})
callback(null, response);
} catch (err) {
callback(err, null);
}
}
OR
function ServerSend(endpoint,params,callback) {
// most callbacks are two parameters - 1st erro or null, second data if no error is present.
axios.get(host+endpoint, {params}).then(data => callback(null, data)).catch(err => callback(err, null));
}

Module sending back api data

I'm working on a module containing a class with functions which calls an api everytimes we use a function.
I want to handle the result & catch IN the module and only send data in return.
function getAllFromTable(aTableName){
const request = {
.... aTableName
}
return apiCall(request)
}
function apiCall(requestConfig){
axios(requestConfig).then(result => {
return result.data
}
.catch(err => {
return err
}
}
That's the idea of what I would like to do but of course this only sends back "undefined"..
Is there a way to make a return in a then() to send back the data ?
Or another way to send back only the data and not a Promise to the one who calls the function "getAllFromTable()" ?
Is there a way to make a return in a then() to send back the data ? Or another way to send back only the data and not a Promise to the one who calls the function "getAllFromTable()" ?
No. In Javascript, there is NO way to return an asynchronously retrieved result directly. You HAVE to use an asynchronous mechanism such as a promise, a callback or an event to communicate back the asynchronous result. Since you already have a promise, you need to just return the promise and make sure that its resolved value is your final result. Then, the caller can use either .then() or await to get the result out of the promise.
That's the idea of what I would like to do but of course this only sends back "undefined"..
In your apiCall() function, you have to return the promise and fix your error handling.
Change this:
function apiCall(requestConfig){
axios(requestConfig).then(result => {
return result.data
}.catch(err => {
return err
});
}
to this:
function apiCall(requestConfig){
return axios(requestConfig).then(result => {
return result.data;
});
}
I removed the .catch() because you were changing the promise from rejected to resolved and making the error object be the resolved value. That is unlikely what you want for the function behavior and very unlikely what the caller wants. If you want a .catch() to log the error or something, then you can throw the error again to make sure the promise stays rejected.
function apiCall(requestConfig){
return axios(requestConfig).then(result => {
return result.data;
}).catch(err => {
// log error and rethrow so the promise stays rejected
console.log(err);
throw err;
});
}
The caller of apiCall() will then either use .then() or await to get the resolved value of the promise that the function returns.
apiCall(...).then(val => {
console.log(val);
}).catch(err => {
console.log(err);
});

Node.js Promise without returning it

I have a use case to resolve a Promise without returning it. Catching for errors internally, but don't want the caller to wait for the promise to resolve.
doSomething()
{
Promise.resolve()
.then(() => {
// do something.
})
.catch(reason => {
this.logger.error(reason);
});
}
Getting this error:
(node:2072) Warning: a promise was created in a handler at internal/timers.js:439:21 but was not returned from it, see http://. goo.gl/rRqMUw
at Function.Promise.cast (.../node_modules/bluebird/js/release/promise.js:225:13)
Just return something from the Promise callback where you are creating the fire and forget promise.
I'm guessing that handler is doSomething
doSomething()
{
Promise.resolve()
.then(() => {
// do something.
})
.catch(reason => {
this.logger.error(reason);
});
return null //or anything else that's sensible
}
Note: We usually ignore the error message, but sometimes they contain valuable information. In your error there's a link http://. goo.gl/rRqMUw that explains exactly this problem:d

Why is the error not being caught in this NodeJS async function?

I am trying to understand why the catch block in the upload function does not capture the exception thrown after the request(.....) line in createReleaseVersion function.
The nodejs script crashes and the exception is unhandled. Only the error of the exception is shown in the console.
In the following code, I expected to be printed 'got you' then 'after', but it does not happen.
If I replace throw with return reject (... same object) then I get the desired output.
I get first printed 'got you' then 'after'
function createReleaseVersion(releaseVersion) {
var options = {
uri: 'https://someurl',
method: 'POST',
json: {'version': releaseVersion}
};
return new Promise(function(resolve, reject) {
request(options, function (error, response, body) {
throw {
error: error,
response: response,
body: body
};
console.log(body);
if (error) {
throw {
error: error,
response: response,
body: body
};
} else {
resolve();
}
});
});
}
async function upload(releaseVersion) {
try {
await createReleaseVersion(releaseVersion);
} catch (error) {
console.log('got you');
}
console.log('after');
}
upload('ddd');
Well, let's take a simpler case:
new Promise((resolve, reject) => {
setTimeout(() => { throw new Error(); });
});
Which crashes the process. The reason is that the setTimeout (or request in your case) subscribes a callback to be called later.
When the callback is eventually called - the promise constructor is done executing already and the error is thrown into the current context (setTimeout/request's).
The reason the promise constructor catches errors synchronously is because it basically does this:
try {
executor(resolve, reject); // your code
} catch (e) {
reject(e);
}
Because in your case the function does not execute synchronously - it has no way to know about the exception's context. This might change in the future with Zones but probably will not.
If you want to mark an error inside a promisified function - you can call the second argument (reject) and reject(new Error(...)).
I warmly recommend you use util.promisify instead of converting APIs to promises manually to avoid these sort of issues :)
I also recommend you only reject with Error objects for better stack traces.

Resources