How use async/await with error reading sensor? - javascript

Comment: I'm sure this is quite simple, but I can't seem to figure out the right combination of async/await try/catch.
Senario: I'm reading DHT22 temp/humidity sensor that may return an error, in which case I want to return a default value. I want getHumidity() to wait for reading and return value or default value. And then printConditions() simple calls and doesn't execute until it receives a response.
Question: Is it possible have delay in getHumidity(), and other calls are unaware its async, cause I have a lot of variations of printConditions()?
const printConditions = () => `Current Humidity is: ${getHumidity().fixed(2)}`;
//Both Attempts return: Current Humidity is NaN%
//Which I believe implies it is not waiting nor default value of 75.0.
//Attempt 1
const getHumidity = async () => {
try { return await sensor.read(22, sensorPin).humidity; }
catch (error) {console.log(error); return 75.0; }
}
try/catch block returns this error: ??? : (node:1368) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
//Attempt 2:
const getHumidity = async () => {
return await sensor.read(22, sensorPin)
.then(value=>{ return value.humidity;})
.catch(error=>{console.log(error); return 75.0;});
}

const printConditions = async () => {
let reading;
try {
reading = await sensor.read(22, sensorPin).humidity;
}
catch(error) {
console.log(error);
reading = 75.0;
}
finally {
console.log(`Current Humidity is: ${reading.fixed(2)}`);
}
}

did you try adding an await when you call the asynchronus function?
const printConditions = async () => `Current Humidity is: ${await getHumidity().fixed(2)}`;

Related

Force a controlled rejection in promise.allSettled()

I am trying to force a rejection for a promise.allSettled() function in a controlled way.
The idea is run a series of urls in batches through an API, this API from time to time returns a 500 error for a given request and it can be safetly retried. So I want to trigger a rejection on promise.allSettled() where I can collect the failing urls and later on rerun then on a recursion.
Batchrequest function
export async function batchRequest(poolLimit, array, iteratorFn, exception) {
const promises = []
const racers = new Set()
for (const item of array) {
const pro = Promise.resolve().then(() => iteratorFn(item, array))
promises.push(pro)
racers.add(pro)
const clean = () => racers.delete(pro)
pro.then(clean).catch(clean)
if (racers.size >= poolLimit) await Promise.race(racers)
}
const results = await Promise.allSettled(promises)
// Collect errors rejected by iteratorFn,
const rejected = results
.filter(({ status, reason }) => status === 'rejected' && reason.name === exception)
.map(({ reason }) => reason.error)
// Recurse the array of rejected urls
if (rejected.length) {
await batchRequest(poolLimit, rejected, iteratorFn, exception)
}
}
Here we run the promises as normal but collect all rejected urls, I am trying to use the exception 'timeout' as the rule to determine if it needs to be rerun as it was just a timeout error.
Iterator function
async function runRequest(url) {
try {
const { data } = await axios('https://exampleAPI.com')
// Take the data and write it somewhere...
} catch (error) {
if (error.response.status === 500) {
throw { name: 'timeout', url }
}
}
})
const urls = [...many urls]
await batchRequest(100, urls, runRequest, 'timeout')
I am getting an error saying
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "#<Object>".] { code: 'ERR_UNHANDLED_REJECTION' }
How can I force a controlled rejection on promise.allSettled()?
UPDATE-----
I found that the unhandled rejection was at the point in which I started the batchrequest
await batchRequest(100, urls, runRequest, 'timeout')
It needs a try catch here:
const urls = [...many urls]
try {
await batchRequest(100, urls, runRequest, 'timeout')
} catch (error) {
console.log(error)
}
but the whole point was to use the promise.allSettled() to absorb the error and not get out of the batchrequest
It looks to me like you're not handling the exception thrown by the runRequest or the first call to iteratorFn. That's what's causing the "unhandled exception" error.
I'd suggest to run this code through a debugger and go line by line until you find the line causing you to get that exception thrown.
Just manually handle each promise and collect errors and results and return or throw when all promises resolved:
const errors = [];
const results = [];
const promises = [
Promise.resolve(1),
Promise.resolve(2),
Promise.reject("error1"),
Promise.reject("error2"),
Promise.resolve(3),
];
Promise.all(
promises.map(p => p.then(r => results.push(r)).catch(e => errors.push(e)))
).then(() => {
console.log({results, errors});
});

Handle exceptions within a returned promise

I know this is not the most beautiful code, but due to legacy issues, I need to stick to this workflow.
The problem is that I am not able to bubble up any exceptions that might occur in the heart of the returned promise.
The code is designed that both the reject and resolve return valid data. So, if you change the const CONDITION to 0.4, we will be getting a rejection. If the value of const CONDITION stays at 0.6, we will be getting a resolution. This works so far.
However, in cases where we have structural failures, such as in the example below where we try to pass a wrong variable name into the rejection:
let reasoxxxx = '__FAILED__';
reject({error: reason, data: output});
I am not able to invoke a throw to bubble up the error. For that reason, I am getting the usual ugly message and I cannot bubble up a proper exception:
Exchange request was rejected due to error(s).
(node:224) UnhandledPromiseRejectionWarning: ReferenceError: reason is not defined
(node:224) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:224) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Any ideas? The code snipped should work.
function fakeFetch() {
// Promisify the request.
return new Promise((resolve, reject) => {
// Emulate an asynchroneous fetch.
setTimeout(() => {
let result = 0.4; // Change to 0.4 to trigger a failed fetch.
if (result < 0.5) {;
reject('__FAIL__');
} else {
resolve({name: 'apple', price: '1234.12', time: 1549926859970});
}
}, 2000);
});
}
async function sendExchangeRequest(id, pair, symbols, callback)
{
let err, result
await fakeFetch().then((output) => { result = output }).catch((error) => {err = error})
if(err){
result = 'None'
}else{
err = 'None'
}
callback(err, result)
}
async function fetchExchangeData(id, pair, symbols) {
// Promisify the request.
try {
return new Promise((resolve, reject) => {
try {
// Send the request.
sendExchangeRequest(id, pair, symbols, ((err, output) => {
try{
if(err){
// Soft Failure
console.log('Exchange request was rejected due to error(s).');
reject({error: err, data: output});
}else{
// Success
console.log('Exchange request was successful.');
resolve({error: err, data: output});
}
} catch(error) {
throw error;
}
}));
} catch(error) {
console.log('---\n', error, '\n---');
throw error;
}
});
} catch(error) {
// Bubble up the error?
console.log('+++\n', error, '\n+++');
throw error;
}
}
(async () => {
await fetchExchangeData('myid', 'MYPAIR', 'mySymbol')
.then((result) => console.log(result))
.catch((failure) => console.log(failure))
})();
--- EDIT (01) ---
I have updated my example snipped to include a fake API call. I hope this makes my question a bit more clear.
I think you're not understanding the concept of async functions. Your first try/catch in fetchExchangeData do essentially nothing, and there is nothing async in it. The try/catch inside the initial promise, is also almost useless, as I'm sure most/all issues with sendExchangeRequest will likely be dealt with the error handler function (err). As for the try/catch blocks, you shouldn't throw errors inside the promise, you should instead reject the error (Which will bubble it up). Only try/catch if you're in an async function, and NOT using callbacks.
function fetchExchangeData(id, pair, symbols) {
// Promisify the request.
return new Promise((resolve, reject) => {
try {
// Send the request.
sendExchangeRequest(id, pair, symbols, ((err, output) => {
try{
if(err){
// Soft Failure
console.log('Exchange request was rejected due to error(s).');
let reason = '__FAILED__';
reject({error: reason, data: output});
}else{
// Success
console.log('Exchange request was successful.');
resolve({error: '__NONE__', data: output});
}
} catch(error) {
reject(error);
}
}));
} catch(error) {
console.log('---\n', error, '\n---');
reject(error);
}
});
}

Class - return data via await?

I am learning how to use class with Async/Await. I think I am doing something wrong with getData function in the Run class.
It should output "Hello World" when using await get() (experiment).
When I run the script, I get an error:
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This
error originated either by throwing inside of an async function
without a catch block, or by rejecting a promise which was not handled
with .catch(). (rejection id: 1)
Class Script:
class Run {
constructor() {
this.stop = false;
}
async getData(entry) {
if (this.stop) {
console.log("Stopped")
return;
}
return await this.get();
}
async get() {
return "Hello World";
}
stop() {
this.stop = true;
}
}
Usage:
let run = new Run();
run.getData(async (entry) => {
console.log(entry);
});
You're getting an error because you forgot the this qualifier:
async getData(entry) {
if (this.stop) {
^^^^
Using return await only makes sense, when you use it within a try/catch block. Otherwise, it is completely redundant.
You should use it here instead. Also, getData does not use its parameter entry. You should call it directly:
console.log(await run.getData());
^^^^^
Your original code refer to the other methods, not the one in your class:
async getData(entry) {
if (stop) { // <--- this refers to this.stop() method
console.log("Stopped")
return;
}
return await get(); <--- this doesn't refer to your `this.get()` method
}
So, add this. to fix it at above two positions.

How to do error handling with async/await without having to await

Take the following contrived example:
const housekeepingStuff = async function (data) {
const result = await notImportant(data);
result.more = 'yawn';
storeInDatabase(result);
};
const getStuff = async function () {
try {
const data = await getData();
data.extra = 'wow';
housekeepingStuff(data); // <---- don't want to await... but need to for error catching
return Promise.resolve(data);
} catch (err) {
return Promise.reject(err);
}
};
try {
const myData = await doSomeStuff();
res.send(myData);
} catch (err) {
console.log(err);
res.sendStatus(400);
}
I want to return the data from getStuff () ASAP without waiting for housekeepingStuff() but if I don't await that function then I have an uncaught error.
I could call housekeepingStuff() outside the getStuff() function, after getting and sending the data to whoever wants it:
try {
const myData = await doSomeStuff();
res.send(myData);
await housekeepingStuff(data); // <---- am awaiting but who cares because nothing follows
} catch (err) {
console.log(err);
res.sendStatus(400);
}
But that doesn't seem right because I don't want to have to remember to call housekeepingStuff() every time I call doSomeStuff()... it should ideally be handled "internally".
What is the correct approach here?
A promise (or async) function has 2 possible outcomes:
A successful outcome
An error outcome
To get either outcome, you must wait for it. You can't wait for 1 condition and not for the other, because the entire thing needs to execute so you can find out what the outcome was.
Otherwise you're really asking the javascript engine: Please predict for me if the function will fail, and if it does, await it.
The correct approach therefore is to just await it.
However, if you don't care about either successful or failed outcomes of this function, just call the function via another async function that eats all the errors:
async function doSomeStuffAndIgnoreError() {
try {
await doSomeStuff();
} catch (e) {
console.error(e);
}
}

how to reject promise inside a then

I want to be able to reject the entire promise chain if anyone of the promise fails in a clean way. I want to "catch" this rejection and send an error notification. I have implemented it the following code:
let reportMetaData = api.ajaxGet(api.buildV3EnterpriseUrl('reports' + '/' + params.report_id))
.catch(error => {
if (error.status === constants.HTTP_STATUS.GATEWAY_TIMEOUT) {
this.notify.error(this.translate('reports.report_timedout'), this.translate('reports.report_timedout_desc'));
} else {
this.send('error', error);
}
});
let aggregateData = reportMetaData.then(success => {
try {
return api.xmlRequest('GET', success.aggregationUrls.elements[0].url);
} catch (error) {
return Promise.reject();
}
}).then(rawData => {
try {
return JSON.parse('{' + rawData + '}');
} catch (error) {
return Promise.reject();
}
}, error => Promise.reject(error));
let aggregateReport = aggregateData.then(data => {
if (!data || !data.report) {
return Promise.reject();
}
return data.report;
}).catch(error =>{
this.notify.error(this.translate('reports.report_timedout'), error);
});
As you can see, it is super messy and complicated. Is there a way I can simplify this? I want the simplest way to reject the entire promise to fail if anyone promise fails. How do I do that from inside the then function? Also, it seems like the thrown error is bubbling up all the way to chrome console as uncaught error. Why does it bubble up even though I caught it?
You need to use Promise.all() and provide the array of promises as input parameter.
If one of those promises will fail, all the promises will not be resolved.
Here the doc:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
And here a post on SO where you can read about them:
When to use promise.all()?
Try aggregating everything under Promise.all(iterable).
More here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
If this is not quite what you wanted, take a look at Bluebird - fully featured promise library. Here
UPDATE: If you want to reject the entire promise if any of the promises inside the function fails, try implementing:
throw validationError;
Hope it works.
You could use async functions to clean things up a bit. I think you could replace your code with the following.
async function processDataAndReport() {
try {
const data = await api.ajaxGet(api.buildV3EnterpriseUrl('reports' + '/' + params.report_id));
const rawData = await api.xmlRequest('GET', data.aggregationUrls.elements[0].url);
const { report } = JSON.parse(`{${rawData}}`);
} catch(e) {
// send notification
}
}

Categories