I am new to NodeJS and kind of an amateur at Javascript. I have written a recursive function called 'maskFields' that goes through a very LARGE Json object with many properties and sub-properties, and checks which fields in the Json object are "sensitive data" by checking them against the second parameter, an array of strings. If a field in the Json object exists in the array, then it is a sensitive field, and I mask it's value with asterisks. I want this function to run ASYNCHRONOUSLY, and this is how I've tried to do it:
function isEmpty(value) {
return (value == null || (typeof value === "string" && value.trim().length === 0));
}
async function maskFields(jsonObject, fieldsToMask) {
if (typeof jsonObject === 'object' && jsonObject !== null) {
if (Array.isArray(jsonObject)) {
for (let i = 0; i < jsonObject.length; i++) {
jsonObject[i] = await maskFields(jsonObject[i], fieldsToMask);
}
} else {
for (const key in jsonObject) {
if (jsonObject.hasOwnProperty(key)) {
jsonObject[key] = await maskFields(jsonObject[key], fieldsToMask);
if (fieldsToMask.includes(key) && !isEmpty(jsonObject[key])){
if(typeof jsonObject[key] !== "string")
{
jsonObject[key] = String(jsonObject[key]);
}
var data = jsonObject[key].substring(0, 1);
var maskedChar = '*'.repeat(jsonObject[key].length - 1);
var output = data + maskedChar;
jsonObject[key] = output;
}
}
}
}
}
return jsonObject; **//THIS IS COMING BACK COMPLETELY EMPTY!**
}
As you can see, the first parameter to the recursive function maskFields, 'jsonObject' is the LARGE Json object I am recursively iterating through, and the second parameter 'fieldstoMask' is the array that contains the sensitive keys whose values need to be masked. If the jsonObject contains a sensitive field that exists in 'fieldsToMask', my code will mask it with asterisks. In the end, I want to return the JSON object with all sensitive fields masked.
HOWEVER, the jsonObject being returned is completely empty. If I remove the async/await, it works fine and returns what I want. Obviously, I am doing something wrong with the async and await. Can anyone tell me how to fix this?
HOWEVER, the jsonObject being returned is completely empty.
Your maskFields()
function returns a promise that resolves to your jsonObject. It does not directly return your jsonObject. You are probably looking at the promise and see no enumerable fields so you think you have an empty object. But, in reality, you need to use .then()
or await
to get the resolved value from the promise that maskFields()
returns. Keep in mind that all async
functions return a promise.
But, this isn't the real issue here at all. You're misusing async
and await
on code that isn't asynchronous and isn't using promises. So, async
and await
here are of no use at all. They don't magically make something run asynchronously.
I want to make it asynchronous because the jsonObject I will actually be passing in to parse is extremely large, and so I do not want the function to run synchronously.
As I've stated in the comments, you cannot make synchronous code into asynchronous code in nodejs using only Javascript. The only actual asynchronous operations in nodejs are those who have an underlying library implementation in native code that uses either asynchronous OS APIs (networking in nodejs) or uses native threads to present an asynchronous appearance to Javascript code (file I/O in nodejs) or code that just calls existing asynchronous APIs.
Promises are just a tool to help you coordinate and track completion and errors in asynchronous operations. They don't, by themselves, make anything asynchronous. That can't make synchronous code run asynchronously. And, async/await
are just tools to help you use promises more easily. await
does absolutely nothing useful unless you are awaiting a promise that is tied to some underlying asynchronous operation. Since you have no actual asynchronous code in your maskFields()
function, neither promises or async/await
are of any use.
So, you cannot just rewrite this in Javascript in nodejs in some way to make it non-blocking, asynchronous.
I can think of four possible options for you:
1. Instrument your code to find out how long this actually takes to run. It's not complicated code. It likely does not take long to run and you may be trying to solve a problem that isn't actually a problem.
2. Write this code so that is can do small pieces of work in chunks, allow other things to run, then do another chunk, etc... To write code this way, you typically end up creating some sort of state machine object that keeps track of your state and then you do one small piece of work, pause briefly to allow any other things in the event queue to run (often with a short duration timer), then do another piece of work, etc... until done. This allows you to make steady progress on a long running operation without blocking other things from running and doing their job. You're essentially doing manual time slicing in the single main thread by making your code run only in short bursts.
3. Move this code to a WorkerThread and then communicate back the final result using the process messaging that WorkerThreads support. In this way, you can run the synchronous implementation you already have (after you remove the promises and async/await
) and it will not block the main thread.
4. Move this code to a child process which can also be a nodejs app if you want and communicate back the final result using the interprocess messaging that child processes support. In this way, you can run the synchronous implementation you already have (after you remove the promises and async/await
) and it will not block your nodejs process.