How can I run await in the beforeAll function? - javascript

I'm trying to run some async functions before any test in a specific file is ran. I tried doing the following:
describe('api/user', () => {
let user;
const userObj = {...};
beforeAll(async () => {
user = await new User(userObj).save(); // This is a mongoose document
});
...
});
Whenever I have something in the beforeAll function I get an error:
Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.
I tried changing the timeout time to 30 seconds and that didn't fix it. I then tried adding the done function before the end of the beforeAll function, and it didn't fix the issue either.
How can I run await in the beforeAll function?

That a block with async function results in test timeout even with very long timeout values means that there's pending promise that never resolves. Adding done to async functions is not a viable option because it cannot improve this but can also result in more timeouts in case done is never called.
This is a known case for Mongoose models, it's known for chaining connection promise internally. If there's no connection, model operations return pending promises.
The solution is to establish a connection before other operations. In case a connection is shared for the test suite, it should be:
beforeAll(async () => {
await mongoose.connect(...);
user = await new User(userObj).save();
});

I ran into a similar problem and had to explicitly pass in and call the done function:
describe('api/user', () => {
let user;
const userObj = {...};
beforeAll(async (done) => {
user = await new User(userObj).save(); // This is a mongodb document
done();
});
...
});

Related

Jest "Async callback was not invoked within the 5000 ms timeout" with monkey-patched `test` and useFakeTimers

This setup is extremely specific but I couldn't find any similar resources online so I'm posting here in case it helps anyone.
There are many questions about Jest and Async callback was not invoked, but I haven't found any questions whose root issue revolves around the use of jest.useFakeTimers(). My function should take no time to execute when using fake timers, but for some reason Jest is hanging.
I'm using Jest 26 so I'm manually specifying to use modern timers.
This is a complete code snippet that demonstrates the issue.
jest.useFakeTimers('modern')
let setTimeoutSpy = jest.spyOn(global, 'setTimeout')
async function retryThrowable(
fn,
maxRetries = 5,
currentAttempt = 0
) {
try {
return await fn()
} catch (e) {
if (currentAttempt < maxRetries) {
setTimeout(
() => retryThrowable(fn, maxRetries, currentAttempt + 1),
1 * Math.pow(1, currentAttempt)
)
}
throw e
}
}
describe('retryThrowable', () => {
const fnErr = jest.fn(async () => { throw new Error('err') })
it('retries `maxRetries` times if result is Err', async () => {
jest.clearAllMocks()
const maxRetries = 5
await expect(retryThrowable(() => fnErr(), maxRetries)).rejects.toThrow('err')
for (let _ in Array(maxRetries).fill(0)) {
jest.runAllTimers()
await Promise.resolve() // https://stackoverflow.com/a/52196951/3991555
}
expect(setTimeoutSpy).toHaveBeenCalledTimes(maxRetries)
})
})
The full error message is
Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error: Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.
at mapper (../../node_modules/jest-jasmine2/build/queueRunner.js:27:45)
Any ideas would be very appreciated
edit 1: I have tried --detectOpenHandles but no new information is provided
edit 2: I just tried my above code snippet in a fresh project and realized that it passes just fine. So the issue must somewhere else in my Jest config. I'll answer my own question when I determine the root cause
My issue ended up being in my jest configuration.
We execute tests directly against an in-memory DB, and to keep our tests clean we wrap each test in a DB transaction. Jest doesn't provide a native aroundEach hook like many other test runners, so we achieved this by monkey-patching the global test and it functions so we could execute the test callback inside a transaction. Not sure if it matters but to be explicit we are using Sequelize as our ORM and for transactions.
The test I was executing (as seen above) recursively called setTimeout with a function which threw an error / rejected a Promise. Sequelize transactions apparently do not appreciate unhandled rejections, and it was causing the test to hang. I never was able to get to the root of why the test was hanging; the transaction successfully rolled back and all test expectations were run, but for some reason the test never exited.
Solution #1 (not great)
My first solution is not pretty but it is pragmatic. I simply extended the Jest test function with a variant which does not use the monkey-patched test.
// jest.setup.ts
declare namespace jest {
interface It {
noDb: (name: string, fn?: ProvidesCallback, timeout?: number) => void
}
}
it.noDb = it
// jest.config.js
module.exports = {
// ...
setupFilesAfterEnv: [
'./jest.setup.ts', // <-- inject `it.noDb` method
'./jest.mokey-patch.ts', // <-- monkey-patching
],
}
Then, I modified the test from the OP to call this new function
it.noDb('retries `maxRetries` times if result is Err', ...
More details on how and why this extension works can be found in this blog post.
Solution #2 (better)
After messing with this more, I realized that the root issue was that there were unhandled promise rejections happening in the main thread. I'm not sure why this conflicted with Sequelize Transactions but suffice to say it's bad practice anyway.
I was able to avoid the issue entirely, as well as any bizarre Jest extensions, by simply fixing the method to only throw on the first call. This way, we can handle errors when we call retryThrowable but do not throw errors on subsequent calls.
// ...
try {
return await fn()
} catch (e) {
if (currentAttempt < maxRetries) {
setTimeout(
() => retryThrowable(fn, maxRetries, currentAttempt + 1),
1 * Math.pow(1, currentAttempt)
)
}
// 💡 this is the new part
if (currentAttempt === 0) {
throw e
}
}
// ...

How can I execute a function right after an "await" request has been completed?

This whole thing about Promises and async, etc. is driving me insane. I just need to run a function whenever I connect to the database. (This code works fine, but I can't know when the await is complete. Help!
async function connectToDB() {
console.log("Connecting to database...");
database = await pool.connect();
// I want to run some function only after the connection has been made.
}
I'm working with node.js and postgreSQL as the database manager.
If pool.connect() return a promise that resolves when it's done, then you just put your function call on the next line right after the await and it won't get called until pool.connect() resolves its promise.
If you're not using a version of your database where pool.connect() returns a promise, then you need to switch to a different module for your database that supports promises.
And, of course, if the pool.connect() promise rejects, then it will skip the rest of the function (analagous to a throw) and the caller of your funiiton will see a rejection on the promise that your async function returns.
async function connectToDB() {
console.log("Connecting to database...");
database = await pool.connect();
callSomeFunctionHere(); // this will happen after pool.connect() resolves
}
Async functions return a Promise and when you await an async function, you're indicating that that the script should not continue until that function is complete
You can replace // I want to run some function only after the connection has been made. with your code and that code will only run after pool.connect() is completed.
You can also await connectToDb() as you've made it an async function as well.
await connectToDb();
// other code that is run immediately after connectToDb finishes
// --- OR ---
connectToDb().then(() => {
// other code that is run immediately after connectToDb finishes
});

Async / await vs then which is the best for performance?

I have a simple code in JavaScript that execute a request in an API and return the response, simple. But in this case I will have thousands of requests. So, which one of the code options will perform better, and why. Also which one is recommended as good pratices these days?
First options is using the .then to resolve the promises and the seccond one is using async / await.
In my tests the two options had very similar results without significant differences, but I'm not sure in scale.
// Using then
doSomething(payload) {
const url = 'https://link-here/consultas';
return this.axios.get(url, {
params: {
token: payload.token,
chave: payload.chave,
},
}).then(resp => resp.data);
}
// Using Async / await
async doSomething(payload) {
const url = 'https://link-here/consultas';
const resp = await this.axios.get(url, {
params: {
token: payload.token,
chave: payload.chave,
},
});
return resp.data;
}
Any explanation will be of great value.
From a performance point of view, await is just an internal version of .then() (doing basically the same thing). The reason to choose one over the other doesn't really have to do with performance, but has to do with desired coding style or coding convenience. Certainly, the interpreter has a few more opportunities to optimize things internally with await, but its unlikely that should be how you decide which to use. If all else was equal, I would choose await for the reason cited above. But, I'd first choose which made the code simpler to write and understand and maintain and test.
Used properly, await can often save you a bunch of lines of code making your code simpler to read, test and maintain. That's why it was invented.
There's no meaningful difference between the two versions of your code. Both achieve the same result when the axios call is successful or has an error.
Where await could make more of a convenience difference is if you had multiple successive asynchronous calls that needed to be serialized. Then, rather than bracketing them each inside a .then() handler to chain them properly, you could just use await and have simpler looking code.
A common mistake with both await and .then() is to forget proper error handling. If your error handling desire in this function is to just return the rejected promise, then both of your versions do that identically. But, if you have multiple async calls in a row and you want to do anything more complex than just returning the first rejection, then the error handling techniques for await and .then()/.catch() are quite different and which seems simpler will depend upon the situation.
There should be some corrections in this thread. await and .then are going to give very different results, and should be used for different reasons.
await will WAIT for something, and then continue to the next line. It's also the simpler of the two because it behaves mechanically more like synchronous behavior. You do step #1, wait, and then continue.
console.log("Runs first.");
await SomeFunction();
console.log("Runs last.");
.then splits off from the original call and starts operating within its own scope, and will update at a time the original scope cannot predict. If we can put semantics aside for a moment, it's "more asynchronous," because it leaves the old scope and branches off into a new one.
console.log("Runs first.");
SomeFunction().then((value) => {console.log("Runs last (probably). Didn't use await on SomeFunction().")})
console.log("Runs second (probably).");
As more explanation to #user280209 answer let's consider the following function which returns promise and compare its execution with .then() and async await.
function call(timeout) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`This call took ${timeout} seconds`);
resolve(true);
}, timeout * 1000);
});
}
With .then()
(async () => {
call(5).then((r) => {
console.log(r);
});
await call(2); //This will print result first
await call(1);
})();
When running the above call the logs will be
This call took 2 seconds
This call took 1 seconds
This call took 5 seconds
true
As we can see .then() didn't pause the execution of its below line until it completes.
With async/wait
(async () => {
await call(5); //This will print result first
await call(2);
await call(1);
})();
When run the above function logs will be
This call took 5 seconds
This call took 2 seconds
This call took 1 seconds
So I think if your promise's result won't be used in the following lines, .then() may be better.
For those saying await blocks the code until the async call returns you are missing the point. "await" is syntactic sugar for a promise.then(). It is effectively wrapping the rest of your function in the then block of a promise it is creating for you. There is no real "blocking" or "waiting".
run();
async function run() {
console.log('running');
makePromises();
console.log('exiting right away!');
}
async function makePromises() {
console.log('make promise 1');
const myPromise = promiseMe(1)
.then(msg => {
console.log(`What i want to run after the promise is resolved ${msg}`)
})
console.log('make promise 2')
const msg = await promiseMe(2);
console.log(`What i want to run after the promise is resolved via await ${msg}`)
}
function promiseMe(num: number): Promise<string> {
return new Promise((resolve, reject) => {
console.log(`promise`)
resolve(`hello promise ${num}`);
})
}
The await line in makePromises does not block anything and the output is:
running
make promise 1
promise
make promise 2
promise
exiting right away!
What i want to run after the promise is resolved hello promise 1
What i want to run after the promise is resolved via await hello promise 2
Actually.
Await/Async can perform more efficiently as Promise.then() loses the scope in which it was called after execution, you are attaching a callback to the callback stack.
What it causes is: The system now has to store a reference to where the .then() was called. In case of error it has to correctly point to where the error happens, otherwise, without the scope (as the system resumed execution after called the Promise, waiting to comeback to the .then() later) it isn't able to point to where the error happened.
Async/Await you suspend the exection of the method where it is being called thus preserving reference.
If we just consider performance(time taken) then it actually depends on whether your operations are serial or parallel. If your tasks are serial then there will be no difference between await and .then. But if your tasks are parallel then .then will take less time. Consider the following example
let time = Date.now();
// first task takes 1.5 secs
async function firstTask () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
},1500)
})
}
// second task takes 2 secs
async function secondTask () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
},2000)
})
}
// using await
async function call(){
const d1 = await firstTask();
const d2 = await secondTask();
console.log(Date.now()-time, d1+d2)
}
call()
// using .then
async function call2(){
let d1=null,d2=null;
firstTask().then(data => {
d1=data;
if(d2){
console.log(Date.now()-time, d1+d2);
}
})
secondTask().then(data => {
d2=data;
if(d1){
console.log(Date.now()-time, d1+d2);
}
})
}
call2()
Here are the two tasks, first takes 1.5 secs and second takes 2 secs. Call function uses await where as call2 function uses .then . The output is as follows
From call2 2012 3
From call 3506 3
I hope it helps.
As far as I understand .then() and await are not the same thing. An async function won't proceed with the next command until the promise is resolved/rejected since it's basically an implementation of generators. On the contrast, in the case of .then(), the execution of the function will proceed with the next command and the resolve/reject callback will be executed "when there's time" aka when the current event loop (not entirely sure about that part) will be completed.
tldr; on a single promise await and .then() behave similarly but when one promise needs another one to be resolved first then the two of them behave entirely different
Many answer have been provided to this question already. However, to point out key information in the answers above and from my understanding, note below point:
only use await when not handling error return
if no crucial need for error handling use await instead
use .then .catch if returned error message or data is crucial for debugging / or proper error handling instead of try catch for await
Choose any prefer method from code sample below
const getData = (params = {name: 'john', email: 'ex#gmail.com'}) => {
return axios.post(url, params);
}
// anywhere you want to get the return data
// using await
const setData = async () => {
const data = await getData();
}
// to handle error with await
const setData = async () => {
try {
const data = await getData();
}
catch(err) {
console.log(err.message);
}
}
// using .then .catch
const setData = () => {
var data;
getData().then((res) => {
data = res.data; console.log(data)
}).catch((err) => {
console.log(err.message);
});
}

create knex queryBuilder and execute later

I'm hoping to create a queryBuilder object, and defer execution until I'm ready. The following code allows me to pass a queryBuilder object between functions without executing the query.
const getQueryBuilder = () =>
knex({ client: "mysql2" }).queryBuilder();
const getBaseSelect = () =>
getQueryBuilder().select().from('foo');
const getOne = () =>
getBaseSelect().limit(1);
console.log(getBaseSelect()); // select * from `foo`
console.log(getOne()); // select * from `foo` limit 1
My question is how do I then execute the query that I've constructed in the queryBuilder?
Or is there a better way to construct queries from multiple synchronous functions before executing?
Thanks.
You want to 'defer execution until' you are 'ready.'
It will depend on what you mean by 'ready', but you can fire the query when the desired action is finished using Promise.
For example, if you want to execute the query after 5 seconds delay, using the example from MDN Promise doc, you can do something like this:
let myFirstPromise = new Promise((resolve, reject) => {
// We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
// In this example, we use setTimeout(...) to simulate async code.
// In reality, you will probably be using something like XHR or an HTML5 API.
setTimeout(function(){
resolve("After this value is resolved with 5000ms delay, "); // Yay! Everything went well!
}, 5000);
});
myFirstPromise.then((successMessage) => {
// successMessage is whatever we passed in the resolve(...) function above.
// It doesn't have to be a string, but if it is only a succeed message, it probably will be.
// You can fire your query here
console.log(successMessage + "you can execute your query inside this then(..) function!");
// execute(getBaseSelect());
});
If you are waiting on multiple promises, you can use Promise.all(..) like this:
Promise.all([myFirstPromise, mySecondPromise, myThirdPromise])
.then((values) => {
return execute(values[0], values[1], values[2])
});

Testing a Promise using setTimeout with Jest

I'm trying to understand Jest's asynchronous testing.
My module has a function which accepts a boolean and returns a Promise of a value. The executer function calls setTimeout, and in the timed out callback the promise resolves or rejects depending on the boolean initially supplied. The code looks like this:
const withPromises = (passes) => new Promise((resolve, reject) => {
const act = () => {
console.log(`in the timout callback, passed ${passes}`)
if(passes) resolve('something')
else reject(new Error('nothing'))
}
console.log('in the promise definition')
setTimeout(act, 50)
})
export default { withPromises }
I'd like to test this using Jest. I guess that I need to use the mock timers Jest provides, so my test script looks a bit like this:
import { withPromises } from './request_something'
jest.useFakeTimers()
describe('using a promise and mock timers', () => {
afterAll(() => {
jest.runAllTimers()
})
test('gets a value, if conditions favor', () => {
expect.assertions(1)
return withPromises(true)
.then(resolved => {
expect(resolved).toBe('something')
})
})
})
I get the following error/failed test, whether or not I call jest.runAllTimers()
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Can you explain where I'm going wrong and what I might do to get a passing test that the promise resolves as expected?
The call to jest.useFakeTimers() mocks every timer function with one that you must control. Instead of the timer running automatically, you would advance it manually. The jest.runTimersToTime(msToRun) function would advance it by msToRun milliseconds. It's very common that you want to fast-forward until every timer has elapsed and it would be cumbersome to calculate the time it takes for all the timers to finish, so Jest provides jest.runAllTimers(), which pretends that enough time has passed.
The problem in your test is that you never call jest.runAllTimers() in the test, but you call it in the afterAll hook, which is called after the tests have finished. During your test the timer remains at zero so your callback is never actually called and Jest aborts it after a predefined interval (default: 5s) to prevent being stuck with a potentially endless test. Only after the test has timed out, you call jest.runAllTimers(), at which point it doesn't do anything, since all tests have already finished.
What you need to do is launch the promise and then advance the timer.
describe('using a promise and mock timers', () => {
test('gets a value, if conditions favor', () => {
expect.assertions(1)
// Keep a reference to the pending promise.
const pendingPromise = withPromises(true)
.then(resolved => {
expect(resolved).toBe('something')
})
// Activate the timer (pretend the specified time has elapsed).
jest.runAllTimers()
// Return the promise, so Jest waits for its completion and fails the
// test when the promise is rejected.
return pendingPromise
})
})

Categories