Catching thrown errors with SinonJS, UnhandledPromiseRejectionWarning - node.js

I have the following async function that checks the returned value from a promise and I having trouble writing
async function fetchData(pageLocation) {
const data = await
apiService.fetchPage(pageLocation);
if (!data || !data.mapping) {
const error = new Error(`Unknown channel ${pageLocation}`);
error.code = 404;
throw (error);
}
return data.mapping;
}
Test case
describe.only('fetchData', () => {
let fetchPage;
beforeEach(() => {
fetchPage =
sinon.stub().returns(Promise.resolve(mockMapping));
csfPageService.__set__({
apiService: {
fetchPage,
},
});
});
it('should throw an error when there is no available Data', () => {
channeData', async function() {
const fetchChannelSectionData = pageService.__get__('fetchData');
expect(async () => { await fetchData('pageLocation'); }).to.throw();
expect(fetchPage).to.be.calledWith('pageLocation');
console.log('----------------------2');
});
What causing the main issue is having an async function and a promise I am able to use the same approach when it is not an async function and there is no await I have looked into the following links
Catching thrown errors with SinonJS
https://www.chaijs.com/api/bdd/#method_throw
enter link description here
but I haven't been successful
please advise on how should this be done ...

That is one of the reasons I don't like async, await, they are just syntactic sugar over promises, but they uses normal/sync semantics but just in appearance.
Async functions never throws, no matter how bad is the error you throw inside it, they will just return a rejected promise. In your case, your function is not throwing at all, it is returning a rejected promise, and you are not attaching any catch hanlder to that promise, hence the warning. When you use async function or promises, forget about normal handling of errors, promises catches any error automatically and encapsulates them on a rejected promise.
So, in your case the correc way of doing this will vary depending on your testing framework, but it could be something like this:
it('should throw an error when there is no available Data', () => {
channeData', async function() {
const fetchChannelSectionData = pageService.__get__('fetchData');
fetchData('pageLocation').catch(err => {
expect(err).to.be.an.error();
expect(fetchPage).to.be.calledWith('pageLocation');
console.log('----------------------2');
})
});

Related

NodeJs async SyntaxError [duplicate]

I wrote this code in lib/helper.js:
var myfunction = async function(x,y) {
....
return [variableA, variableB]
}
exports.myfunction = myfunction;
Then I tried to use it in another file :
var helper = require('./helper.js');
var start = function(a,b){
....
const result = await helper.myfunction('test','test');
}
exports.start = start;
I got an error:
await is only valid in async function
What is the issue?
The error is not refering to myfunction but to start.
async function start() {
....
const result = await helper.myfunction('test', 'test');
}
// My function
const myfunction = async function(x, y) {
return [
x,
y,
];
}
// Start function
const start = async function(a, b) {
const result = await myfunction('test', 'test');
console.log(result);
}
// Call start
start();
I use the opportunity of this question to advise you about an known anti pattern using await which is : return await.
WRONG
async function myfunction() {
console.log('Inside of myfunction');
}
// Here we wait for the myfunction to finish
// and then returns a promise that'll be waited for aswell
// It's useless to wait the myfunction to finish before to return
// we can simply returns a promise that will be resolved later
// useless async here
async function start() {
// useless await here
return await myfunction();
}
// Call start
(async() => {
console.log('before start');
await start();
console.log('after start');
})();
CORRECT
async function myfunction() {
console.log('Inside of myfunction');
}
// Here we wait for the myfunction to finish
// and then returns a promise that'll be waited for aswell
// It's useless to wait the myfunction to finish before to return
// we can simply returns a promise that will be resolved later
// Also point that we don't use async keyword on the function because
// we can simply returns the promise returned by myfunction
function start() {
return myfunction();
}
// Call start
(async() => {
console.log('before start');
await start();
console.log('after start');
})();
Also, know that there is a special case where return await is correct and important : (using try/catch)
Are there performance concerns with `return await`?
To use await, its executing context needs to be async in nature
As it said, you need to define the nature of your executing context where you are willing to await a task before anything.
Just put async before the fn declaration in which your async task will execute.
var start = async function(a, b) {
// Your async task will execute with await
await foo()
console.log('I will execute after foo get either resolved/rejected')
}
Explanation:
In your question, you are importing a method which is asynchronous in nature and will execute in parallel. But where you are trying to execute that async method is inside a different execution context which you need to define async to use await.
var helper = require('./helper.js');
var start = async function(a,b){
....
const result = await helper.myfunction('test','test');
}
exports.start = start;
Wondering what's going under the hood
await consumes promise/future / task-returning methods/functions and async marks a method/function as capable of using await.
Also if you are familiar with promises, await is actually doing the same process of promise/resolve. Creating a chain of promise and executes your next task in resolve callback.
For more info you can refer to MDN DOCS.
When I got this error, it turned out I had a call to the map function inside my "async" function, so this error message was actually referring to the map function not being marked as "async". I got around this issue by taking the "await" call out of the map function and coming up with some other way of getting the expected behavior.
var myfunction = async function(x,y) {
....
someArray.map(someVariable => { // <- This was the function giving the error
return await someFunction(someVariable);
});
}
I had the same problem and the following block of code was giving the same error message:
repositories.forEach( repo => {
const commits = await getCommits(repo);
displayCommit(commits);
});
The problem is that the method getCommits() was async but I was passing it the argument repo which was also produced by a Promise. So, I had to add the word async to it like this: async(repo) and it started working:
repositories.forEach( async(repo) => {
const commits = await getCommits(repo);
displayCommit(commits);
});
If you are writing a Chrome Extension and you get this error for your code at root, you can fix it using the following "workaround":
async function run() {
// Your async code here
const beers = await fetch("https://api.punkapi.com/v2/beers");
}
run();
Basically you have to wrap your async code in an async function and then call the function without awaiting it.
The current implementation of async / await only supports the await keyword inside of async functions Change your start function signature so you can use await inside start.
var start = async function(a, b) {
}
For those interested, the proposal for top-level await is currently in Stage 2: https://github.com/tc39/proposal-top-level-await
async/await is the mechanism of handling promise, two ways we can do it
functionWhichReturnsPromise()
.then(result => {
console.log(result);
})
.cathc(err => {
console.log(result);
});
or we can use await to wait for the promise to full-filed it first, which means either it is rejected or resolved.
Now if we want to use await (waiting for a promise to fulfil) inside a function, it's mandatory that the container function must be an async function because we are waiting for a promise to fulfiled asynchronously || make sense right?.
async function getRecipesAw(){
const IDs = await getIds; // returns promise
const recipe = await getRecipe(IDs[2]); // returns promise
return recipe; // returning a promise
}
getRecipesAw().then(result=>{
console.log(result);
}).catch(error=>{
console.log(error);
});
If you have called async function inside foreach update it to for loop
Found the code below in this nice article: HTTP requests in Node using Axios
const axios = require('axios')
const getBreeds = async () => {
try {
return await axios.get('https://dog.ceo/api/breeds/list/all')
} catch (error) {
console.error(error)
}
}
const countBreeds = async () => {
const breeds = await getBreeds()
if (breeds.data.message) {
console.log(`Got ${Object.entries(breeds.data.message).length} breeds`)
}
}
countBreeds()
Or using Promise:
const axios = require('axios')
const getBreeds = () => {
try {
return axios.get('https://dog.ceo/api/breeds/list/all')
} catch (error) {
console.error(error)
}
}
const countBreeds = async () => {
const breeds = getBreeds()
.then(response => {
if (response.data.message) {
console.log(
`Got ${Object.entries(response.data.message).length} breeds`
)
}
})
.catch(error => {
console.log(error)
})
}
countBreeds()
In later nodejs (>=14), top await is allowed with { "type": "module" } specified in package.json or with file extension .mjs.
https://www.stefanjudis.com/today-i-learned/top-level-await-is-available-in-node-js-modules/
This in one file works..
Looks like await only is applied to the local function which has to be async..
I also am struggling now with a more complex structure and in between different files. That's why I made this small test code.
edit: i forgot to say that I'm working with node.js.. sry. I don't have a clear question. Just thought it could be helpful with the discussion..
function helper(callback){
function doA(){
var array = ["a ","b ","c "];
var alphabet = "";
return new Promise(function (resolve, reject) {
array.forEach(function(key,index){
alphabet += key;
if (index == array.length - 1){
resolve(alphabet);
};
});
});
};
function doB(){
var a = "well done!";
return a;
};
async function make() {
var alphabet = await doA();
var appreciate = doB();
callback(alphabet+appreciate);
};
make();
};
helper(function(message){
console.log(message);
});
A common problem in Express:
The warning can refer to the function, or where you call it.
Express items tend to look like this:
app.post('/foo', ensureLoggedIn("/join"), (req, res) => {
const facts = await db.lookup(something)
res.redirect('/')
})
Notice the => arrow function syntax for the function.
The problem is NOT actually in the db.lookup call, but right here in the Express item.
Needs to be:
app.post('/foo', ensureLoggedIn("/join"), async function (req, res) {
const facts = await db.lookup(something)
res.redirect('/')
})
Basically, nix the => and add async function .
"await is only valid in async function"
But why? 'await' explicitly turns an async call into a synchronous call, and therefore the caller cannot be async (or asyncable) - at least, not because of the call being made at 'await'.
Yes, await / async was a great concept, but the implementation is completely broken.
For whatever reason, the await keyword has been implemented such that it can only be used within an async method. This is in fact a bug, though you will not see it referred to as such anywhere but right here. The fix for this bug would be to implement the await keyword such that it can only be used TO CALL an async function, regardless of whether the calling function is itself synchronous or asynchronous.
Due to this bug, if you use await to call a real asynchronous function somewhere in your code, then ALL of your functions must be marked as async and ALL of your function calls must use await.
This essentially means that you must add the overhead of promises to all of the functions in your entire application, most of which are not and never will be asynchronous.
If you actually think about it, using await in a function should require the function containing the await keyword TO NOT BE ASYNC - this is because the await keyword is going to pause processing in the function where the await keyword is found. If processing in that function is paused, then it is definitely NOT asynchronous.
So, to the developers of javascript and ECMAScript - please fix the await/async implementation as follows...
await can only be used to CALL async functions.
await can appear in any kind of function, synchronous or asynchronous.
Change the error message from "await is only valid in async function" to "await can only be used to call async functions".

NodeJS - Properly catch errors of async functions

I was writing a script to pull data from Google Cloud metrics via API when I accidentally discovered that I don't know how to properly catch errors of asynchronous functions. :O
Here is the example code from google cloud:
// Imports the Google Cloud client library
const monitoring = require('#google-cloud/monitoring');
// Creates a client
const client = new monitoring.MetricServiceClient();
/**
* TODO(developer): Uncomment and edit the following lines of code.
*/
const projectId = 'XXXXXXXXX';
async function getMetrics() {
const request = {
name: client.projectPath(projectId),
filter: 'metric.type="cloudsql.googleapis.com/database/cpu/utilization"',
interval: {
startTime: {
// Limit results to the last 20 minutes
seconds: Date.now() / 1000 - 60 * 1,
},
endTime: {
seconds: Date.now() / 1000,
},
},
// Don't return time series data, instead just return information about
// the metrics that match the filter
view: 'HEADERS',
};
// Writes time series data
console.log('start')
const [timeSeries] = await client.listTimeSeries(request);
console.log('Found data points for the following instances:');
timeSeries.forEach(data => {
console.log(data.metric.labels.instance_name);
});
}
getMetrics();
The function listTimeSeries returns a promise. I got an error that I need to be authenticated to perform that action, no problem there.
The issue is that I couldn't catch that error.
I tried surrounding the call with try {...} catch (err) {...} block, wasn't caught.
I tried to catch it like this const [timeSeries] = await client.listTimeSeries(request).catch(console.log); - No luck there.
I must be missing something because I'm pretty new to nodeJS and no way catching errors from async functions is not supported.
I'm using nodeJS v14.
What am I missing guys?
Thank you in advance!
EDIT
As requested (by #CherryDT), here is the full error output:
I hope its not too blurry.
EDIT
It turns out that the way I've been trying to catch errors is fine.
The issue occurred because of listTimeSeries function (from an external library), which threw an error instead of rejecting the promise, which is impossible to catch.
Thanks, guys.👍
Note that I refer to "async functions" and "asynchronous functions." In Javascript "async function" means a function created with the async keyword, whereas when I say "asynchronous function" I mean in the traditional sense, any function that runs asynchronously. In Javascript, functions created with the async keyword are actually just promises under the hood.
Your code would work if errors thrown from asynchronous functions (inside promises) could be caught. Unfortunately, they can't. Unless the function is using the async function syntax, errors in promises must be wrapped with reject. See the MDN example for the gotcha we're looking at here:
// Throwing an error will call the catch method most of the time
var p1 = new Promise(function(resolve, reject) {
throw new Error('Uh-oh!');
});
p1.catch(function(e) {
console.error(e); // "Uh-oh!"
});
// Errors thrown inside asynchronous functions will act like uncaught errors
var p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
throw new Error('Uncaught Exception!');
}, 1000);
});
p2.catch(function(e) {
console.error(e); // This is never called
});
// Errors thrown after resolve is called will be silenced
var p3 = new Promise(function(resolve, reject) {
resolve();
throw new Error('Silenced Exception!');
});
p3.catch(function(e) {
console.error(e); // This is never called
});
I believe this is the code in the library that's throwing the error, below. Notice that another error is being properly rejected. All comments are mine.
for (const methodName of metricServiceStubMethods) {
const callPromise = this.metricServiceStub.then(
stub => (...args: Array<{}>) => {
if (this._terminated) {
// This is the right thing to do!
return Promise.reject('The client has already been closed.');
}
const func = stub[methodName];
return func.apply(stub, args);
},
(err: Error | null | undefined) => () => {
// If this was an async function (as in, using the keyword async,
// not just literally an asynchronous function), this would work,
// because the async keyword is just syntactic sugar for creating
// a promise. But it's not so it can't be caught!
throw err;
}
);
I believe, in this case, unfortunately there's no way for you to catch this error.
You can do this.
(async function() {
try {
await getMetrics();
} catch(error) {
console.log("Error occured:", error);
}
})();
Please note that if you are trying to catch the error in Promise you can use .then(() => { }).catch(err => { }) style, but for async/await you will need try { } catch(err) { } style to catch the error.
Edit
By doing this, it must catch any errors if the promise become rejected. If you still cannot catch the error, this means that the library you're using doesn't reject the promise properly (Promise.reject()), instead it did hard-coded throw error inside the promise instead of rejecting one. For this case you can't do anything with error catching.

How to test "functions returning errors" using Jest and Typescript?

How to test fs.writeFile using Jest?
I want to test the following fs.write function using Jest
async function saveFile(): Promise<void | Error> {
fs.writeFile("./test.txt", (await newStockLength()).toString(), (err) => {
if (err) {
console.error(err);
return err;
}
//file written successfully
process.exit(0);
});
}
My Test is:
it("should run no mather what", async () => {
const ran = await saveFile();
expect(ran).resolves.not.toBe(Error);
});
I want to see the error in my test if Node returns an Error object.
How should I add Error Object to Promise<void | ErrorObject > in typescript?
If I use Promise<void | Error> and use toBeNull
expect(ran).resolves.toBeNull();
I get following error:
expect(received).resolves.toBeNull()
Matcher error: received value must be a promise
Received has value: undefined
fs.writeFile does not return a promise. fs.writeFileSync is a good synchronous alternative if you're OK with blocking the event loop.
It looks to me like your code isn't doing quite what you expect it to to. The saveFile function returns a Promise, and the result of the callback has nothing to do with the Promise that's returned, and you're not waiting for the write-function's work to happen. Working with callbacks & Promises in the same function can be a bit hairy, so Node comes with a util.promisify helper.
import {promisify} from "util";
const writeFileAsync = promisify(fs.writeFile);
async function saveFile (): Promise<void | Error> {
try {
await writeFileAsync(...);
} catch (err) {
console.error(err);
return err;
}
}
Alternatively, you can do the same thing by returning a promise:
await new Promise((resolve, reject) => {
fs.writeFile(..., (err) => {
if (err) resolve(err);
return resolve(null);
})
})
In general, though, I'd recommend against functions that return errors like this. Having functions throw errors when they fail is a much more common, safe, and effective pattern.

Is it OK to pass an async function to setImmediate in order to call an arbitrary function asynchronously?

I want to call a given function asynchronously. The wrapper function tryCallAsync is one way of doing this. This approach works. However, it requires that the callback for setImmediate to be an async function. This seems wrong, as the callback is returning a Promise that is not used. Is it wrong to pass an async function to setImmediate for this purpose?
async function tryCallAsync(fn, ...args) {
return new Promise((r, j) => {
setImmediate(async () => {
try {
r(await fn(...args));
}
catch (e) {
j(e);
}
})
})
}
// Using tryCallAsync
let resolveAsync = tryCallAsync(()=>{
return new Promise((r,j)=>{
setImmediate(()=>r('resolveAsync'));
});
})
resolveAsync.then((resolve)=>console.log(resolve));
let resolve = tryCallAsync(()=>{
return 'resolve';
});
resolve.then((resolve)=>console.log(resolve));
NB: https://www.youtube.com/watch?v=e3Nh350b6S4
Yes, it's wrong, for multiple reasons:
setImmediate doesn't handle the returned promise, especially it doesn't deal with errors1
Don't put business logic in asynchronous (non-promise) callbacks when using promises. Settle a promise from there, nothing else.
1: And even while your particular callback never rejects the returned promise due to the try/catch, it still feels wrong
Your function should be written as
async function tryCallAsync(fn, ...args) {
await new Promise(resolve => {
setImmediate(resolve);
});
return fn(...args);
}
This approach doesn't waste a Promise, however, still, it's not as performant as the conventional way of doing this.
function tryCallAsync(fn, ...args) {
return new Promise((r, j) => {
setImmediate(() => {
(async function () {
return await fn(...args);
})().then(r).catch(j);
});
});
}

Promises seem to not be called in correct order?

I am trying to rewrite a module I wrote that seeds a MongoDB database. It was originally working fine with callbacks, but I want to move to Promises. However, the execution and results don't seem to make any sense.
There are three general functions in a Seeder object:
// functions will be renamed
Seeder.prototype.connectPromise = function (url, opts) {
return new Promise((resolve,reject) => {
try {
mongoose.connect(url, opts).then(() => {
const connected = mongoose.connection.readyState == 1
this.connected = connected
resolve(connected)
})
} catch (error) {
reject(error)
}
})
}
[...]
Seeder.prototype.seedDataPromise = function (data) {
return new Promise((resolve,reject) => {
if (!this.connected) reject(new Error('Not connected to MongoDB'))
// Stores all promises to be resolved
var promises = []
// Fetch the model via its name string from mongoose
const Model = mongoose.model(data.model)
// For each object in the 'documents' field of the main object
data.documents.forEach((item) => {
// generates a Promise for a single item insertion.
promises.push(promise(Model, item))
})
// Fulfil each Promise in parallel
Promise.all(promises).then(resolve(true)).catch((e)=>{
reject(e)
})
})
}
[...]
Seeder.prototype.disconnect = function () {
mongoose.disconnect()
this.connected = false
this.listeners.forEach((l) => {
if (l.cause == 'onDisconnect') l.effect()
})
}
There is no issue with the main logic of the code. I can get it to seed the data correctly. However, when using Promises, the database is disconnected before anything else is every done, despite the disconnect function being called .finally().
I am running these functions like this:
Seeder.addListener('onConnect', function onConnect () { console.log('Connected') })
Seeder.addListener('onDisconnect', function onDisconnect () {console.log('Disconnected')})
Seeder.connectPromise(mongoURI, options).then(
Seeder.seedDataPromise(data)
).catch((error) => { <-- I am catching the error, why is it saying its unhandled?
console.error(error)
}).finally(Seeder.disconnect())
The output is this:
Disconnected
(node:14688) UnhandledPromiseRejectionWarning: Error: Not connected to MongoDB
at Promise (C:\Users\johnn\Documents\Code\node projects\mongoose-seeder\seed.js:83:37)
which frankly doesn't make sense to me, as on the line pointed out in the stack trace I call reject(). And this rejection is handled, because I have a catch statement as shown above. Further, I can't understand why the database never even has a chance to connect, given the finally() block should be called last.
The solution was to return the Promise.all call, in addition to other suggestions.
You are passing the wrong argument to then and finally. First here:
Seeder.connectPromise(mongoURI, options).then(
Seeder.seedDataPromise(data)
)
Instead of passing a callback function to then, you actually execute the function on the spot (so without waiting for the promise to resolve and trigger the then callback -- which is not a callback).
You should do:
Seeder.connectPromise(mongoURI, options).then(
() => Seeder.seedDataPromise(data)
)
A similar error is made here:
finally(Seeder.disconnect())
It should be:
finally(() => Seeder.disconnect())
Promise Constructor Anti-Pattern
Not related to your question, but you are implementing an antipattern, by creating new promises with new Promise, when in fact you already get promises from using the mongodb API.
For instance, you do this here:
Seeder.prototype.connectPromise = function (url, opts) {
return new Promise((resolve,reject) => {
try {
mongoose.connect(url, opts).then(() => {
const connected = mongoose.connection.readyState == 1
this.connected = connected
resolve(connected)
})
} catch (error) {
reject(error)
}
})
}
But the wrapping promise, created with new is just a wrapper that adds nothing useful. Just write:
Seeder.prototype.connectPromise = function (url, opts) {
return mongoose.connect(url, opts).then(() => {
const connected = mongoose.connection.readyState == 1
this.connected = connected
return connected;
});
}
The same happens in your next prototype function. I'll leave it to you to apply a similar simplification there, so avoiding the promise constructor antipattern.
In the later edit to your question, you included this change, but you did not return a promise in that function. Add return here:
return Promise.all(promises).then(() => {
//^^^^^^
return true
}).catch(() => {
console.log(`Connected:\t${this.connected}`)
})

Resources