I have a function cloudFunctionCall in my cloud-function setup that is invoked on every cloud function call.
import * as functions from "firebase-functions";
const mysql = require("mysql");
function cloudFunctionCall(asyncFunc) {
return functions
.region(region)
.https.onCall(async (data: any, context: any) => {
try {
const pool = mysql.createPool(getConfig(context));
const result = await asyncFunc(data, pool);
pool.end();
res.send(result);
} catch (err) {
log("A promise failed to resolve", err);
res.status(500).send(err);
}
});
}
export const func1 = cloudFunctionCall(_func1);
export const func2 = cloudFunctionCall(_func2);
context and therefore pool defines to which database an invoked function needs to talk to which might change on every cloud function call. Now I want to avoid passing pool as a parameter to asyncFunc since it is not actually required for the function logic and makes the functions more verbose.
Now my question: Is it safe to define pool as a global variable that gets updated on every cloud function call like the following?
//...
let pool;
function cloudFunctionCall(asyncFunc) {
return functions
.region(region)
.https.onCall(async (data: any, context: any) => {
try {
pool = mysql.createPool(getConfig(context));
const result = await asyncFunc(data, pool);
//...
I understand that every exported function is hosted in it's own environment but what would happen if func1 would get executed twice in quick succession on different databases? Could it happen that the second invocation overwrites pool that was set by the first invocation so that the latter one executes on the wrong database?
If so, is there another way to avoid passing pool as a parameter?
Cloud Function invocations run in strict isolation of each other: each container will only ever have one execution at a time. So there is no chance of function invocations overwriting the value of your global for each other.
Related
I am trying to mock the AWS SDK. I have a function, ingest that does some logic (including some calls to the AWS SDK sqs.deleteMessage function) and then calls ingestAndSave (which also makes some calls to the sqs.deleteMessage function).
When testing the ingest function, I want to mock my ingestAndSave function so I can have those tests focus purely on the ingest function logic.
Then I want to test the ingestAndSave function logic in its own describe block - and test the real thing, not the mocked version.
When testing either ingest or ingestAndSave functions I need to use my mocked sqs.deleteMessage
My most recent attempt includes following the path here: https://www.lucasamos.dev/articles/jestmocking. My test suite looks like this:
const ingest = require('./ingest')
const TEST_CONSTANTS = require('../../../tests/constants')
const mockdeleteMessage = jest.fn().mockImplementation( ()=> {
console.log("MOCK DELETE MESSAGE FUNCTION");
return {}
} )
jest.mock('aws-sdk', () => {
return {
config: {
update() {
return {}
}
},
SQS: jest.fn(() => {
return {
deleteMessage: jest.fn(( { params } )=> {
return {
promise: () => mockdeleteMessage()
}
}),
}
})
}
});
describe("FUNCTION: ingest", () => {
let ingestAndSaveFunctionSpy
beforeEach(()=> {
ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
Promise.resolve() )
})
test("ingestAndSave will be called two times when event has two Records", async () => {
await ingest.ingest(TEST_CONSTANTS.sqsIngestEventMessageWithTwoRecords)
expect(ingestAndSaveFunctionSpy).toHaveBeenCalledTimes(2)
})
afterEach(() => {
jest.resetAllMocks()
})
afterAll(() => {
jest.restoreAllMocks()
})
})
describe("FUNCTION: ingestAndSave", () => {
let validParams = {camera: TEST_CONSTANTS.validCameraObject, sourceQueueURL:"httpexamplecom", receiptHandle: "1234abcd"}
test("Will call deleteMessage once when given valid paramters", async () => {
await ingest.ingestAndSave(validParams)
expect(mockdeleteMessage.mock.calls.length).toBe(1)
})
/** snip other tests */
})
Both of the above functions run through code that looks just like this:
let deleteResp;
try {
deleteResp = await sqs.deleteMessage({ QueueUrl: sourceQueueURL, ReceiptHandle: receiptHandle}).promise()
} catch (err) {
console.error('Task: Deleting message from the ingest queue: ', err, 'deleteResp: ', deleteResp, 'receiptHandle: ', receiptHandle)
}
The mocked sqs.deleteMessage is used for the first describe block (i.e. I see the console.log for it) and the test passes.
However, the mocked sqs.deleteMessage function is not used for the second describe block (i.e. I do not see the console.log message indicating the mocked function ran, and, in fact, I get a 5000ms timeout, indicating the real sqs.deleteMessage was called (with invalid authorization, the deleteMessage command takes >5 seconds).
I thought the jest.restoreAllMocks() in the afterAll block of the first describe is restoring my mock. So I go with explicitly restoring the ingestAndSaveFunctionSpy mock with ingestAndSaveFunctionSpy.mockRestore() instead. Unfortunately, this results in the same behavior: the mocked AWS SDK is used in the first describe block, but it has been restored by the ingestAndSaveFunctionSpy.mockRestore() call.
Just to test, I remove the afterAll in the first describe entirely. This results in the second test calling the mocked implementation of ingestAndSave and thus the test failing.
Declare the jest.mock... within each describe block, but this isn't allowed due to jest.mock calls getting hoisted to the top of the file.
How can I mock a module using jest.mock(... and have it persist between describe blocks while allowing mockRestore() calls to other mocked functions?
How to change mock implementation on a per single test basis? has me looking at mockImplemention but I'm struggling to see how I'd implement it.
See related question attempting to tackle this from a different angle: How to have jest.mock persist between tests?
If you only want to reset the one mock, then don't reset all of the mocks. Just reset the spy:
describe("FUNCTION: ingest", () => {
let ingestAndSaveFunctionSpy
beforeEach(()=> {
ingestAndSaveFunctionSpy = jest.spyOn(ingest, 'ingestAndSave').mockReturnValue(
Promise.resolve() )
})
// ...
afterEach(() => {
ingestAndSaveFunctionSpy.mockRestore()
})
})
In the outer scope, I would also advise using a:
afterEach(() => {
jest.clearAllMocks()
})
This will reset the recordings of the mock calls to your aws-sdk mock without actually changing any of the mocked behavior. Otherwise, you might be verifying behavior in one test that actually happened during the execution of another.
Without seeing the code, it's hard to offer more specific advice, but this style of test is a code smell, IMO. Any time you have to spy on your own unit in a unit test, it's usually a sign of poor organization. Try to arrange it so that discreet units of work are discreet functions. As an abstract example, instead of chaining functions together like:
const a = (arg) => {
const stuff = /* do some stuff with arg */
b(stuff);
};
const b = (arg) => {
const stuff = /* do some stuff with arg */
c(stuff);
}
const c = (arg) => {
/* do some stuff with arg */
}
Instead make them discreet, testable units that are joined together in the external function:
const abc = (arg) => {
const firstStuff = a(arg);
const secondStuff = b(firstStuff);
c(secondStuff);
}
const a = (arg) => {
return /* do some stuff with arg */
}
const b = (arg) => {
return /* do some stuff with arg */
}
const c = (arg) => {
/* do some stuff with arg */
}
Now you can write tests for a, b, and c independently without having to mock your own internals.
I solved this by wrapping all calls to aws-skd functions in my own functions and then mocking my wrapper functions.
So now I have a utility functions in utils.js that looks like this:
module.exports.sendMessage = async (params) => {
return await sqs.sendMessage(params).promise()
}
And in my real function code, I call it this way (instead of calling sqs.sendMessage directly - a super-easy change):
await utils.sendMessage(sendMessageParams).catch(err => throw (err))
And then during testing, I mock it like this - either in a beforeEach or directly before my test:
sendMessageSpy = jest.spyOn(utils, 'sendMessage').mockReturnValue(Promise.resolve({success:true}))
Later, rinse, and repeat for all aws-sdk functions I want to mock.
I saw in one tutorial this code snippet:
const myFunction = () => {
return function (caller) {
caller(firstFuctnion());
caller(secondFunction());
};
};
Could you please tell me how it works once I call it like:
myFunction()
How come that I don’t get an error when the caller argument is actually not defined?
On the other hand, if I omit it and write the code like this:
const myFunction = () => {
return function () {
firstFuctnion();
secondFunction();
};
};
those two functions firstFuction() and secondFunction() aren’t executed. So, how exactly does that caller — or however it’s called — work?
For those of you who might want to see the whole functioning code:
const redux = require("redux");
const thunkMiddleware = require("redux-thunk").default;
const axios = require("axios");
const createStore = redux.createStore;
const applyMiddleware = redux.applyMiddleware;
const initialState = {
loading: false,
users: [],
error: "",
};
const FETCH_USERS_REQUEST = "FETCH_USERS_REQUEST";
const FETCH_USERS_SUCCESS = "FETCH_USERS_SUCCESS";
const FETCH_USERS_FAILURE = "FETCH_USERS_FAILURE";
const fetchUsersRequest = () => {
return {
type: FETCH_USERS_REQUEST,
};
};
const fetchUsersSuccess = (users) => {
return {
type: FETCH_USERS_SUCCESS,
payload: users,
};
};
const fetchUsersFailure = (error) => {
return {
type: FETCH_USERS_FAILURE,
payload: error,
};
};
const fetchUsers = () => {
return function (dispatch) {
dispatch(fetchUsersRequest());
axios
.get("https://jsonplaceholder.typicode.com/users")
.then((response) => {
// response.data is the users
const users = response.data.map((user) => user.id);
dispatch(fetchUsersSuccess(users));
})
.catch((error) => {
// error.message is the error message
dispatch(fetchUsersFailure(error.message));
});
};
};
const reducer = (state = initialState, action) => {
console.log(action.type);
switch (action.type) {
case FETCH_USERS_REQUEST:
return {
...state,
loading: true,
};
case FETCH_USERS_SUCCESS:
return {
loading: false,
users: action.payload,
error: "",
};
case FETCH_USERS_FAILURE:
return {
loading: false,
users: [],
error: action.payload,
};
}
};
const store = createStore(reducer, applyMiddleware(thunkMiddleware));
store.subscribe(() => {
console.log(store.getState());
});
store.dispatch(fetchUsers());
I don't get this part. What that dispatch actually does. It is nowhere passed as an argument and it is not defined anywhere what it is. Is it function? I could write whatever there instead.
const fetchUsers = () => {
return function (dispatch) {
dispatch(fetchUsersRequest());
axios
.get("https://jsonplaceholder.typicode.com/users")
.then((response) => {
// response.data is the users
const users = response.data.map((user) => user.id);
dispatch(fetchUsersSuccess(users));
})
.catch((error) => {
// error.message is the error message
dispatch(fetchUsersFailure(error.message));
});
};
};
Let’s consider a simplified version of this code.
Before you added some important context to your question (the fact that myFunction() is actually being used, and how it’s used), I wasn’t quite sure where your confusion was, so this answer starts with a general explanation of callbacks, higher-order functions, and currying.
First, let’s turn the function into an arrow function, so the code is more straight-forward to read.
There are differences between them, but let’s ignore them for now.
Let’s also rename myFunction to outer, so we know which function we’re talking about.
const outer = () => {
return (caller) => {
caller(something);
};
};
outer is a function.
outer() is also a function; that means, the return value of the function outer is, itself, a function.
Since outer is a function that returns a function, this makes outer a higher-order function (HOF) (according to one of the two definitions of HOFs)1.
To be explicit, you can reference the returned function by using another variable:
const inner = outer();
How come that I don’t get an error when the caller argument is actually not defined?
That’s a strange question to ask; when outer() is called, it doesn’t touch on caller yet.
Only calling inner does.
Ask yourself this: why do you not get an error for a and b not being defined here?2
const sum = (a, b) => a + b;
sum;
That’s right: sum isn’t even called!
Just like outer() isn’t even called.
Even one step further, this won’t throw an error even if somethingUndefined isn’t defined anywhere and cannot be set anywhere.
(But you will get linter warnings.)
const someFunction = () => somethingUndefined;
someFunction;
Why do you not get an error here, in this common example of HOFs, despite b not being defined at the point where add(5) is assigned to addFive?
const add = (a) => {
return (b) => a + b;
};
// Or more simply:
// const add = (a) => (b) => a + b;
const addFive = add(5);
console.log(addFive(3)); // Pints 8.
Because the function that needs b, i.e. addFive, has not been called yet.
If you want to force an error due to caller not being defined, you’d have to call the function that actually needs caller defined. The function in question is outer()! You call it, like any other function, with (): outer()(). Now you get a TypeError because caller isn’t a function.
This “double call” is called currying.
The code can also be rewritten like this.
The only difference is that inner is now also accessible in the outer scope where outer is.
See What is the scope of variables in JavaScript?.
const inner = (caller) => {
caller(something);
};
const outer = () => inner;
inner takes the role of the returned function (outer()).
It takes a function as an argument and calls it with something as an argument.
outer is just a function that returns the function inner and nothing else.
It does not call it, it doesn’t touch on caller, it has nothing to do with something.
outer() and inner are identical.
On the other hand, if I omit it and write the code [without caller], those two functions firstFuction() and secondFunction() aren’t executed. So, how exactly does that caller — or however it’s called — work?
First, let’s fix the semantics: firstFuction() and secondFunction() are function calls.
Whether they are functions or not, depends on what firstFuction and secondFunction return.
firstFuction and secondFunction are functions (at least the code implies that they are).
More specifically, consider alert:
alert is a function; you can call it like alert().
alert() is not a function; you cannot call it like alert()().
caller is a callback function.
If you omit caller from the code, then myFunction()() would be the proper call that doesn’t throw an error and simply calls firstFuction and secondFunction.
But the point of the callback is to pass the results of firstFuction and secondFunction to the function caller.
You have to provide that function yourself, e.g. the alert function.
myFunction()(alert) calls the inner function, passing alert to it.
The inner function calls firstFuction and secondFunction and passes their results to the caller, which we specified to be alert.
Could you please tell me how it works once I call it like myFunction()?
Quite simply, nothing happens here.
myFunction returns a function, and that’s it; that function isn’t used further, so it is discarded.
There is a key aspect of higher-order functions that isn’t demonstrated in this code: encapsulating values.
Consider this code:
const add = (a) => {
let count = 0;
return (b) => {
++count;
console.log(`The function to add ${a} to something else has been called ${count} time(s).`);
return a + b
};
};
add and b work similarly to myFunction and caller from before, but now, the outer function also needs an argument, so add() won’t help.
add(5) calls the add function and sets argument a to 5.
Further, add(5)(3) calls the inner, returned function, sets b to 3 and returns 8.
But there’s also some state inside add in the form of a variable called count.
const addFive = add(5);
console.log(addFive(3)); // Prints 8.
// But also logs: "The function to add 5 to something else has been called 1 time(s)."
console.log(addFive(10)); // Prints 15.
// But also logs: "The function to add 5 to something else has been called 2 time(s)."
count is only accessible inside outer (including any function inside of it); it is “encapsulated”.
This is a common way to hide some state that should only be accessible inside a specific function.
a is actually also encapsulated inside add(…); a and count have the same scope and aren’t that different from each other, just that a can be set as an argument when calling add whereas count cannot.
You’ve then added more context, where the call looks more like this:
someOtherFunction(myFunction())
This is different from the situation before where you just wrote the call like myFunction().
Now, the result is not discarded, as it is fed into someOtherFunction (in the original code: store.dispatch).
[caller] is nowhere passed as an argument and it is not defined anywhere.
But now it is the job of someOtherFunction to call myFunction() with the appropriate callback function as an argument.
Now, you don’t pass the function yourself; instead, someOtherFunction does it for you.
Compare this to Where do the parameters in a JavaScript callback function come from?.
If you go back to the sum example function from earlier, then either you pass the arguments yourself:
sum(1, 2);
or some library or framework does it for you:
// Library code
const doTheRightThing = (callback) => callback(1, 2);
// Your code
doTheRightThing(sum);
or a built-in (or host-defined) function does it for you:
[ 1, 2, 3 ].reduce(sum, 0);
It doesn’t matter where these parameters come from or where the function is called from.
But it is called, and it is called with the right arguments somewhere.
1: The two definitions of a higher-order function are
a function that returns a function, or
a function that takes another function as an argument.
outer fits the first definition.
outer() (independently and coincidentally) fits the second definition.
2: Okay, granted, sum() would also not throw an error, but a and b would, nonetheless, be undefined, each. undefined + undefined has semantics that don’t result in an error, but hopefully you get the point: in your original code, myFunction()() would throw an error only because undefined(firstFunction()) isn’t possible. The culprit is the same: caller is undefined.
Functions are treated like any other values, meaning they can be returned from functions.
Calling myFunction will simply return the inner function, which will require a parameter caller to be passed to it when it itself is called.
The inner function will remember any arguments that were passed to it's enclosing function.
An example I use to understand this concept is that of a "multiplier factory" function
const answer = function() {
console.log("The answer is: ");
}
const multiplier = function(factor) {
return function(n){
answer();
console.log( n * factor);
};
}
const twice = multiplier(2);
const triple = multiplier(3);
twice(4); // The answer is: 8
triple(5); // The answer is: 15
In the code above twice and triple are essentially the following
const twice = function(n) {
console.log("The answer is: ");
console.log(n * 2);
}
const triple = function(n) {
console.log("The answer is: ");
console.log(n * 3);
}
twice(4); // The answer is: 8
triple(5); // The answer is: 15
To understand more about these concepts, I suggest reading up on "Higher Order Functions" and "Closures" in Javascript
I think you haven't printed the whole snipet. because, there is no definition to firstFunction() and secondFunction() inside myFunction().
to answer your question, if you want to call the function defined in myFunction(), you need to assign the value to a new variable as it is returning the function. then simple make the function call.
const myFunctionResult = myFunction();
myFunctionResult(someOtherCallerFunction);
but note this will throw an error for the reason mentioned above.
I wanted to mock out a function and make sure it has executed a certain number of times.
The tricky part is that the function I wanted to mock out is part of the returned result of another function
My implementation is roughly like
const theFnIWantedToMock = jest.fn()
jest.mock('../hooks', () => {
const actualHooks = jest.requireActual('../hooks')
return {
...actualHooks,
someHooks() {
return
{
theFnIWantedToMock,
}
}
}
})
describe('test', () => {
it('some test', () => {
//...
expect(theFnIWantedToMock).toHaveBeenCalledTimes(1)
})
})
but Jest is throwing an error saying that Invalid variable access theHookINeedToMock. Does anyone know what is the correct way of doing it?
This problem is described in the documentation:,
A limitation with the factory parameter is that, since calls to jest.mock() are hoisted to the top of the file, it's not possible to first define a variable and then use it in the factory. An exception is made for variables that start with the word 'mock'. It's up to you to guarantee that they will be initialized on time! For example, the following will throw an out-of-scope error due to the use of 'fake' instead of 'mock' in the variable declaration
Prefixing a variable with mock disables Jest check. let or const variables are in temporal dead zone before declaration, that they are accessed when a factory is evaluated results in runtime error in untranspiled ES6. For eagerly evaluated mocked modules a mock needs to be defined inside a factory.
A way to avoid this is to use var declaration that is hoisted and initialize it inside a factory:
var theFnIWantedToMock
jest.mock('../hooks', () => {
const actualHooks = jest.requireActual('../hooks')
theFnIWantedToMock = jest.fn()
return {
...actualHooks,
someHooks: jest.fn().mockReturnValue(theFnIWantedToMock),
}
})
A way to keep a reference to it is to keep it a part of the import:
jest.mock('../hooks', () => {
const actualHooks = jest.requireActual('../hooks')
const theFnIWantedToMock = jest.fn()
return {
...actualHooks,
theFnIWantedToMock,
someHooks: jest.fn().mockReturnValue(theFnIWantedToMock),
}
})
This makes theFnIWantedToMock available as a part of import object, also works for reusable mocks in __mocks__.
so I am having some issues with scopes when it comes to nodejs. I am wondering how you can initialize a variable in the global space, initialize it in a function (private scope) then have it equal to what you initialized it to in the private scope when you call it anywhere in the class. One thing I have noticed is that although you can use the variable in a private scope, when it is called again outside that function it just turns undefined. How can I return the same functions initialized when it was private? This is not my issued code but an example in case the picture isn't clear
let name;
class blah {
static blablah() {
name = josh;
}
console.log(name);
//this will return undefined and not josh
}
What I need in my context:
let genesis;
let jsonChain;
class Blockchain {
constructor() {
//this.chain = [Blockchain.getGenesis()];
console.log(Blockchain.getGenesis());
}
//down the file we find the issued function...
static getGenesis() {
fs.readFile(jsonRoute, 'utf-8', function(err, data) {
if (err) throw err;
jsonChain = JSON.parse(data);
genesis = jsonChain.blocks[0].GENESIS_DATA;
return genesis;
});
//returning here instead inside the callback also yields undefined
//I want to be able to access my contents from json file through
//the genesis variable which is global when I return this function
//but I cannot reach the contents of the callback
}
}
SOLUTION: Make sure to return the promise so that the async function runs later in your program when you call it.
let genesis;
let jsonChain;
class Blockchain {
constructor() {
this.chain = [Blockchain.getGenesis()];
console.log(Blockchain.getGenesis().then(function(genesisData) {
console.log(genesisData); // Genesis block data is here.
}, function(err) {
// This only runs if there was an error.
console.log(err);
}));
}
//down the file we find the solved function...
static getGenesis() {
return new Promise(function(resolve, reject) {
fs.readFile(jsonRoute, 'utf-8', function(err, data) {
if(err) return reject(err);
const jsonChain = JSON.parse(data);
resolve(jsonChain.blocks[0].GENESIS_DATA);
});
});
}
}
module.exports = Blockchain;
Now that you've added the real code, there are a number of issues illustrated there.
if (err) throw err inside a plain asynchronous callback does nothing useful. Nobody can catch that error and thus this should never be used. You need real error handling there. IMO, one should never write that line of code in that circumstance.
return genesis; does nothing useful. That just returns back from the fs.readFile() callback where the internals of fs.readFile() are ignoring any return value. That does not return from the higher level function.
jsonChain = JSON.parse(data); does successfully assign to the higher scoped variable, but any code that is trying to use the jsonChain variable will have no idea when it was assigned a value because fs.readFile() is asynchronous and calls its callback at some future, indeterminate time. So, if you're seeing that jsonChain has no value, that's because you're looking at the variable BEFORE it gets assigned. Basically, this is an anti-pattern. You can't program with asynchronous code that way in node.js. Instead, you need to USE the asynchronous value inside the callback or you need to call some function from within the callback and pass it the value. ONLY in that circumstance do you know WHEN the value is available.
If the top level problem here is that you're trying to return some value you retrieved asynchronously, then you cannot directly do that in Javascript. You can read all about that here where it explains that you have to communicate that asynchronous value back to the caller with a callback, a promise or you can also use an event.
The first problem with your code is that josh is not in quotes. josh is undefined; did you mean 'josh' (in quotes)?
One way is to make your getGenesis() function async, so that you can wait for it to finish assigning the correct value to genesis. This requires wrapping everything inside an anonymous async function:
(async () => {
const fs = require('fs');
let genesis;
let jsonChain;
class Blockchain {
constructor() {
this.chain = [Blockchain.getGenesis()];
console.log(Blockchain.getGenesis());
}
static async getGenesis() {
jsonChain = JSON.parse(await fs.promises.readFile(jsonRoute, 'utf-8'));
genesis = jsonChain.blocks[0].GENESIS_DATA;
}
}
console.log(genesis); // undefined D:
await Blockchain.getGenesis();
console.log(genesis); // not undefined :D
})();
The function doSomethingElse in this example fails to execute since its this has been rebound to window or global (if in Node) due to a contextless call inside app.populateDatabase.
Is there any way to avoid this without referencing app inside every function?
loadDatabase function executes a callback according to a logic statement, if an imaginary database didn't exist, it populates it after loading, then the populateDatabase executes the callback it has been provided.
I cannot rebind the onLoaded argument to app since I don't know where it comes from, and the bind/apply/call abstraction overuse creates quite a mess.
var app = {};
app.loadDatabase = function(onLoaded) {
// If database already exists, only run a callback
var callback = onLoaded;
// If database doesn't exists, populate it, then run a callback.
if (!databaseExists) {
callback = this.populateDatabase.bind(this, onLoaded);
}
this.database = new sqlite.Database("file.db", function(error) {
if (error) { ... }
callback();
})
}
app.populateDatabase = function(onPopulated) {
// Contextless call here. <--------
onPopulated();
}
app.doSomethingElse = function() {
// this != app due to contextless call.
this.somethingElse();
}
app.run = function() {
// Load the database, then do something else.
this.loadDatabase(this.doSomethingElse);
}
app.run();
Just replace this.loadDatabase(this.doSomethingElse);
with this.loadDatabase(() => this.doSomethingElse());. This way you create a new arrow function but then doSomethingElse is called with the right this context.
You could also do .bind but I recommend the arrow function. Here with bind: this.loadDatabase(this.doSomethingElse.bind(this))
In general consider to move to promises & maybe async functions. Then do this:
this.loadDatabase().then(() => this.doSomethingElse());
or better with an async function:
await this.loadDatabase();
this.doSomethingElse();