Getting SQS retrieveMessages resolved in Node - javascript

I am getting a few behaviors I didn't expect when running the example code from the AWS SDK for SQS.
I have the following code which takes a queue URL.
const getMessage = url => {
return sqs.receiveMessage(
{
QueueUrl: url
},
(err, data) => {
if (err) {
console.log(err, err.stack); // an error occurred
} else {
// console.log(data);
if (data.Messages) {
const msg = JSON.parse(data.Messages[0].Body);
console.log("--");
return msg
} else {
console.log("no messages found");
return {};
}
}
}
);
};
const messages = await getMessage(<QUEUE_URL>);
console.log('this statement runs before the other console statements')
I understand from the documentation here:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SQS.html#receiveMessage-property
That the function recieveMessage returns a request. But I don't understand why the console statement after the function call is being run before the console.log within the callback since I am awaiting its response.
The order of the logs makes me think I am missing something with the asynchronous nature of the operation and the wrapper function.
Has anyone run into this before? I have been going in circles on this one for awhile and can't seem to figure out what is going on.

you have to return Promise in order to await its result
const getMessage = url => {
return sqs.receiveMessage(
{
QueueUrl: url
})
.promise()
.then(data=>{
// process data here
})
.catch(e=>{
// process error here
})

Related

Not able to catch exception from promise, error response is undefined

I've got some basic Javascript code that calls a stock API with symbols where the symbols are provided from a simple HTTP call like this:
GET http://localhost:4000/batch_stock_prices/?stocks=12312.
I believe I am misunderstanding the syntax for how to catch an exception from a promise..
An exception gets thrown that 12312 is an invalid symbol which I expect, on the terminal running the node server I see the exception but I'm not able to pass it back in the HTTP response. The error that's passed back in the response is 'undefined'. How can I catch the exception? Do I need a try catch somewhere?
const fetch = require('node-fetch')
const { IEXCloudClient } = require("node-iex-cloud");
const { type } = require('tap');
const iex = new IEXCloudClient(fetch, {
sandbox: true,
publishable: "pk_2f78524e5........23c327e24b5",
version: "stable"
});
'use strict'
async function getCurrentPriceOfBatchStocks(_stock) {
stocks_to_submit = _stock['stocks'];
console.log(stocks_to_submit)
response = await iex
.batchSymbols(stocks_to_submit)
.price()
.catch(function (error) { // <-- doesn't seem to get called
console.log("Exception: " + error);
throw error;
})
return new Promise((resolve, reject) => {
if (response) {
resolve(response)
} else {
reject(response); // <-- response is undefined. why?
}
});
}
const batchStocksSchema = {
querystring: {
type: 'object',
properties: {
stocks: {
type: 'string'
}
},
required: ['stocks']
}
}
module.exports = async function (fastify, opts) {
fastify.get('/batch_stock_prices/', {
schema: batchStocksSchema
}, async function (request, reply) {
try {
var response = await getCurrentPriceOfBatchStocks(request.query)
// console.log(response)
return reply
.code(200)
.send(response);
} catch (e) {
console.log(e)
return reply
.code(400)
.send('Bad Request, exception: ' + e) // outputs: ...exception: undefined
}
})
}
It's hard to say for sure what's wrong without running the code, but there are several issues with the use of async, await, and promises in the code, and with creating implicit globals. (Also various missing ;.) If we sort those out, it may be that whatever error is occurring will stop being obscured. See *** comments:
"use strict"; // *** This has to be at the very beginning of the compilation
// unit, it can't be later in the code as it is in the question
const fetch = require('node-fetch')
const { IEXCloudClient } = require("node-iex-cloud");
const { type } = require('tap');
const iex = new IEXCloudClient(fetch, {
sandbox: true,
publishable: "pk_2f78524e5........23c327e24b5",
version: "stable"
});
async function getCurrentPriceOfBatchStocks(_stock) {
// *** Declare `stocks_to_submit`
const stocks_to_submit = _stock['stocks'];
// *** Declare `response`
const response = await iex.batchSymbols(stocks_to_submit).price();
// *** Don't catch the error, let it propagate; the caller should
// know whether the call succeeded or failed
// *** Don't use `new Promise`, there's no purpose to it
return response;
}
const batchStocksSchema = {
querystring: {
type: 'object',
properties: {
stocks: {
type: 'string'
}
},
required: ['stocks']
}
};
// *** This function never uses `await`, so don't make it `async`
module.exports = function (fastify, opts) {
fastify.get('/batch_stock_prices/', {
schema: batchStocksSchema
}, function (request, reply) { // *** Typically old-style callback APIs don't do
// anything with the promise an `async` function
// returns, so don't pass `async` functions into them
getCurrentPriceOfBatchStocks(request.query)
.then(response => {
// *** No `return` here, we aren't resolving the promise from `then` with the result
// of `send`
reply
.code(200)
.send(response);
})
.catch(e => {
console.log(e);
// *** No `return` here, we aren't resolving the promise from `catch` with the
// result of `send`
reply
.code(400)
.send('Bad Request, exception: ' + e);
});
});
};
For why the catch is not called in this part:
response = await iex
.batchSymbols(stocks_to_submit)
.price()
.catch(function (error) { // <-- doesn't seem to get called
console.log("Exception: " + error);
throw error;
})
and why response is undefined:
return new Promise((resolve, reject) => {
if (response) {
resolve(response)
} else {
reject(response); // <-- response is undefined. why?
} });
This is the cause:
The promise returned by price() call had resolves an undefined value (instead of rejecting with an error). Your "await" wait for this undefined value and assigned it to "response" variable.
The price() when having problem have already handled the error and then print the details in the console:
Error: <html>
<head><title>400 Bad Request</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<hr><center>nginx</center>
</body>
</html>
at IEXRequest.<anonymous> (/home/runner/ArtisticAridSite/node_modules/node-iex-cloud/lib/request.js:128:35)
at step (/home/runner/ArtisticAridSite/node_modules/node-iex-cloud/lib/request.js:32:23)
at Object.next (/home/runner/ArtisticAridSite/node_modules/node-iex-cloud/lib/request.js:13:53)
at fulfilled (/home/runner/ArtisticAridSite/node_modules/node-iex-cloud/lib/request.js:4:58)
It wasn't really passing the error back in the chain to your code.
So on your question of "How can I catch the exception?". Unfortunately you probably has no way to receive the exception details (unless you can control the error handling behaviour of iex). You may consider to check whether the result is undefined and handle accordingly.

How to cleanly exit from a second level function using next in nodeJS express?

Below is my stylized code :
let the_function = (req, res, next) => {
Promise
.map(promises_params, (x) => {
return get_promise(x);
}, {concurrency: 20}
).then((values) => {
let reduceFunc = (acc, currV, currI) => {
if (something_bad) {
next(new Error("something bad happened");
return;
} else {
return acc;
}
};
let result = values.reduce(reduceFunc,[]);
res.send(result);
}).catch((err) => {
res.send(err);
});
};
The idea is that I am running a bunch of promises then reducing the values I get to a single object which I then send back to the user.
When something_bad happens within the reduce function reduceFunc, I would like to get a proper error sent back to the user. But in my current setup, the node.js server fails and has to reboot after I get :
_http_outgoing.js:494
throw new ERR_HTTP_HEADERS_SENT('set');
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
and the user gets an error which is not the one I would like her to see.
How to make sure the error I want the user to see is output cleanly without killing the server in the meantime ?
The reason you are running into an error is because calling next does not stop the reduce function from executing. The reduce function continues to run and eventually res.send(results) is called. This becomes a problem because the next route handler is apparently also sending a response, but only one response can be sent for a request so you get an error.
The fact that you are not calling next in your catch handler indicates that you don't really want to use next. Since that is the case you can simply throw the error so that execution of the reduce function is haulted and the catch handler is invoked.
let the_function = (req, res, next) => {
Promise
.map(promises_params, (x) => {
return get_promise(x);
}, {concurrency: 20}
).then((values) => {
let reduceFunc = (acc, currV, currI) => {
if (something_bad) {
throw new Error("something bad happened");
} else {
return acc;
}
};
let result = values.reduce(reduceFunc,[]);
res.send(result);
}).catch((err) => {
res.send(err);
});
};
Now a response is only sent once. It is sent if the reduce function completes without something_bad happening, or when an error occurs, but never both.

Handling network errors with axios and Twilio

I have an application that uses axios for it's ajax requests. When a user experiences a network issue (for example, their wifi goes out and they no longer have an internet connection while on my application), I want to make sure that only the first axios request is made, and if I detect there is a network issue, to not attempt any more requests, but instead to retry the same request until successful.
My application performs many requests, including a request every 2.5 seconds (in this example, getData). It also establishes a Twilio connection when the application initializes (it executes twilio() on initialization).
When a connection is lost, the following happens:
getData fails, resulting in a console message of this is a network error.
TwilioDevice.offline is executed. This results in two error messages: first a this is a network error. message (error message #2) when TwilioDevice.offline tries fetchToken(), and then a received an error. message (error message #3) after the fetchToken() fails.
Given #'s 1 and 2, how can I make sure that:
If I experience a network error, I only receive one error message instead of 3 saying that "there was a network error"
My app detects that there is a network error, then tries to re-establish a connection, then, if successful, resumes fetching data, Twilio tokens, etc.
Thanks! Code is below.
example code:
const getData = async () => {
try {
const response = await axios.get('api/data');
return response.data;
} catch (error) {
handleError(error);
}
};
const fetchToken = async () => {
try {
const data = await axios.get('api/twilio-token');
return data.token;
} catch (error) {
return handleError(error);
}
};
const handleError = error => {
if (!error.response) {
console.log("this is a network error.");
} else {
console.log("received an error.");
}
};
twilio.js:
import { Device as TwilioDevice } from 'twilio-client';
const registerEvents = () => {
TwilioDevice.ready(() => {
console.log('Twilio.Device is now ready for connections');
});
TwilioDevice.connect((conn) => {
console.log(`Connecting call with id ${conn.parameters.CallSid}`);
// code to start call
conn.disconnect((connection) => {
console.log(`Disconnecting call with id ${connection.parameters.CallSid}`);
// code to end call
});
});
TwilioDevice.error((error) => {
console.log("Twilio Error:");
console.log(error);
});
TwilioDevice.offline(async () => {
try {
const newTwilioToken = await fetchToken(); // error message #2
return TwilioDevice.setup(newTwilioToken);
} catch (error) {
return handleError(error); // error message #3
}
});
};
export const twilio = async () => {
try {
registerEvents();
const twilioToken = await fetchToken();
TwilioDevice.setup(twilioToken);
} catch (error) {
return handleError(error);
}
};
I would recommend making your fetchToken and getData methods to throw errors rather than handling it themselves so that they can be handled by their outer functions.
Something like,
const getData = async () => {
try {
const response = await axios.get('api/data');
return response.data;
} catch (error) {
throw (error);
}
};
const fetchToken = async () => {
try {
const data = await axios.get('api/twilio-token');
return data.token;
} catch (error) {
throw (error);
}
};
So that when you call twilio() that function can handle the error like retrying etc.

Javascript : Continue execution after ajax error

I'm trying to accomplish the following:
On load of the page, the code should do a $.getJSON request (which
basically is an ajax get request) (on success yayy just continue execution otherwise go down this list).
when this fails with code 400 I would like to wait 1 second and retry this request (if this succeeds than yayy continue code execution otherwise go down this list)
after that just skip the ajax get request and continue executing the other code.
But Currently, I can't manage to continue to execute the code because of the thrown error. For this I tried :
try{
$.getJSON('/services/getData').success(function(data) {
configurationObject = data["configuration"];
})
} catch(err) {
console.log("error");
}
and
$.getJSON('/services/getData').success(function(data) {
configurationObject = data["configuration"];
})
.error(function(){
console.log("keep running please")
}
but both just stops the execution of the complete javascript code. Is there any way I can keep running after an error occurred when using ajax calls?
Try getting used to promises for doing asynchronous tasks. this is how i would have done it if i where using jQuery
function getData() {
return $.getJSON('/services/getData').then(function(data){
configurationObject = data["configuration"];
return configurationObject
}, function(err) {
var dfd = $.Deferred();
if (err.status === 400) {
? setTimeout(function () {
dfd.resolve(getData());
}, 1000)
} else {
dfd.reject(err)
}
return dfd.promise();
})
}
getData().then(successFn, errorFn)
but if i could get lose on using es7 and async/await and just vanilla javascript then this is how i would have done it instead
const sleep = delay => new Promise(rs => setTimeout(rs, delay))
async function getData() {
const res = await fetch('/services/getData')
if (res.status === 400) {
await sleep(1000)
return getData()
} else if (!res.ok) {
throw new Error('Could not get data')
}
const json = await res.json()
configurationObject = json['configuration']
return configurationObject
}
getData().then(successFn, errorFn)
Please see http://api.jquery.com/jQuery.getJSON/
.error handler has been removed. You can use .fail instead, whose handler receives a jqXhr argument. From that, you can read status to check for 400 etc.

Return Meteor.http results in method

I have a Meteor method that wraps around an http.get. I am trying to return the results from that http.get into the method's return so that I can use the results when I call the method.
I can't make it work though.
Here's my code:
(In shared folder)
Meteor.methods({
getWeather: function(zip) {
console.log('getting weather');
var credentials = {
client_id: "string",
client_secret: "otherstring"
}
var zipcode = zip;
var weatherUrl = "http://api.aerisapi.com/places/postalcodes/" + zipcode + "?client_id=" + credentials.client_id + "&client_secret=" + credentials.client_secret;
weather = Meteor.http.get(weatherUrl, function (error, result) {
if(error) {
console.log('http get FAILED!');
}
else {
console.log('http get SUCCES');
if (result.statusCode === 200) {
console.log('Status code = 200!');
console.log(result.content);
return result.content;
}
}
});
return weather;
}
});
For some reason, this does not return the results even though they exist and the http call works: console.log(result.content); does indeed log the results.
(Client folder)
Meteor.call('getWeather', somezipcode, function(error, results) {
if (error)
return alert(error.reason);
Session.set('weatherResults', results);
});
Of course here, the session variable ends up being empty.
(Note that this part of the code seems to be fine as it returned appropriately if I hard coded the return with some dummy string in the method.)
Help?
In your example Meteor.http.get is executed asynchronously.
See docs:
HTTP.call(method, url [, options] [, asyncCallback])
On the server, this function can be run either synchronously or
asynchronously. If the callback is omitted, it runs synchronously and
the results are returned once the request completes successfully. If
the request was not successful, an error is thrown
Switch to synchronous mode by removing asyncCallback:
try {
var result = HTTP.get( weatherUrl );
var weather = result.content;
} catch(e) {
console.log( "Cannot get weather data...", e );
}
Kuba Wyrobek is correct, but you can also still call HTTP.get asynchronously and use a future to stop the method returning until the get has responded:
var Future = Npm.require('fibers/future');
Meteor.methods({
getWeather: function(zip) {
console.log('getting weather');
var weather = new Future();
var credentials = {
client_id: "string",
client_secret: "otherstring"
}
var zipcode = zip;
var weatherUrl = "http://api.aerisapi.com/places/postalcodes/" + zipcode + "?client_id=" + credentials.client_id + "&client_secret=" + credentials.client_secret;
HTTP.get(weatherUrl, function (error, result) {
if(error) {
console.log('http get FAILED!');
weather.throw(error);
}
else {
console.log('http get SUCCES');
if (result.statusCode === 200) {
console.log('Status code = 200!');
console.log(result.content);
weather.return(result);
}
}
});
weather.wait();
}
});
There's not really much advantage to this method over a synchronous get in this case, but if you're ever doing something on the server which can benefit from something like an HTTP call running asynchronously (and thus not blocking the rest of the code in your method), but you still needs to wait for that call to return before the method can, then this is the right solution. One example would be where you need to execute multiple non-contingent gets, which would all have to wait for each other to return one by one if executed synchronously.
More here.
Sometimes asynchronous calls are preferable. You can use async/await syntax for that, and you need to promisify HTTP.get.
import { Meteor } from 'meteor/meteor';
import { HTTP } from 'meteor/http';
const httpGetAsync = (url, options) =>
new Promise((resolve, reject) => {
HTTP.get(url, options, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
Meteor.methods({
async 'test'({ url, options }) {
try {
const response = await httpGetAsync(url, options);
return response;
} catch (ex) {
throw new Meteor.Error('some-error', 'An error has happened');
}
},
});
Notice that meteor test method is marked as async. This allows using await operator inside it with method calls which return Promise. Code lines following await operators won't be executed until returned promise is resolved. In case the promise is rejected catch block will be executed.

Categories

Resources