Correct way to call function synchronously - node.js

Let me start of by saying I know about async/await but I dont really want to include babel, it seems like to much trouble and I dont have any problem sticking with the promises. So what I am trying to do is pretty straightforward,
basically achieve 'synchronous' flow in my function.
The code below gives me an unhandled exception. I'd like to hear any ideas why is that as well, if possible, whether I am on the right track here. If you have any questions please go ahead and ask.
function A()
{
//...
result = B();
Promise.all(result).then(function(result){
//after finishing B continue
});
}
function B()
{
//..
C();
return number;
}
function C()
{
var data1;
var data2;
//..
calling_DB = DB_get(..., function(result){ data1 = ..;});//callback cause I ma fetching data from DB
Promise.all(data1).then(function(data1){
calling_DB2 = DB_get(..., function(result){ data2 = ..;});
Promise.all(data2).then(function(data2){
//...
});
});
}

You can follow the below approach for calling those functions in the chain
function A()
{
return B()
.then(function(_merged_db_get_results)
{
//after finishing B continue
console.dir(_merged_db_get_results);
return true;
})
.catch(function(error)
{
console.dir(error);
return false;
});
}
function B()
{
return C()
// Can be removed : START
.then(function(_merged_db_get_results)
{
return _merged_db_get_results;
});
// Can be removed : END
}
function C()
{
var db_1_res;
return Promise.resolve(true)
.then(function(_above_true)
{
return DB_get(condition);
})
.then(function(_db_get_results_1)
{
db_1_res = _db_get_results_1;
return DB_get(condition);
})
.then(function(_db_get_results_2)
{
return [db_1_res, _db_get_results_2];
});
}

Related

checking when a function in required file is used

I use a js file with a couple of functions in node which works great. But one of this functions needs to trigger a function in my main js file, and I cannot figure out how to solve this.
So I have my main.js
const externalObject = require('objectJS');
let myfunctions = require('./stuff/myFunctions.js');
myFunctions.doSomething();
and the myFunctions.js
module.exports = {
doSomething: function() {
doSomethingElse();
return data;
}
}
function doSomethingElse() {
//do some stuff
if (a) {
return something;
} else {
externalObject.action()
//or
//inform main.js to do something with externalObject
}
}
My Problem is that I have no idea how to access that external object in myFunctions.js (I cannot require it there), or how to inform my main.js that this object needs to be updated.
Any advice on this one?
Pass the function externalObject.action itself as a parameter to doSomethingElse.
const externalObject = require('objectJS');
let myfunctions = require('./stuff/myFunctions.js');
myFunctions.doSomething(externalObject.action);
The module needs to process the callback parameter accordingly:
module.exports = {
doSomething: function(callback) {
doSomethingElse(callback);
return data;
}
}
function doSomethingElse(callback) {
//do some stuff
if (a) {
return something;
} else {
callback()
//or
//inform main.js to do something with externalObject
}
}
By doing so, externalObject.action is called from inside the module and you can update the externalObject from within the externalObject.action as needed.
Just for others that may use it: After a lot of trying using eventemitter is also a solution:
myFunctions.js
let EventEmitter = require('events').EventEmitter
const myEmitter = new EventEmitter();
module.exports = {
myEmitter,
doSomething: function() {
doSomethingElse();
return data;
}
}
function doSomethingElse() {
//do some stuff
if (a) {
return something;
} else {
myEmitter.emit('action');
}
}
and in main.js
const externalObject = require('objectJS');
let myfunctions = require('./stuff/myFunctions.js');
myfunctions.myEmitter.on('action', function() {
externalObject.Action();
})
Works like a charm.

How to call a custom function synchronously in node js?

I am developing a server project which needs to call some functions synchronously. Currently I am calling it in asynchronous nature. I found some similar questions on StackOverflow and I can't understand how to apply those solutions to my code. Yet I tried using async/await and ended up with an error The 'await' operator can only be used in an 'async' function
Here is my implementation
function findSuitableRoom(_lecturer, _sessionDay, _sessionTime, _sessionDuration, _sessionType){
let assignedRoom = selectRoomByLevel(preferredRooms, lecturer.level, _sessionDuration); <------- Need to be call synchronously
if (!assignedRoom.success){
let rooms = getRooms(_sessionType); <------- Need to be call synchronously
assignedRoom = assignRoom(_lecturer.rooms, _sessionDuration, _lecturer.level);
} else {
arr_RemovedSessions.push(assignedRoom.removed)
}
return assignedRoom;
}
function getRooms(type){
switch (type){
case 'Tutorial' : type = 'Lecture hall'
break;
case 'Practical' : type = 'Lab'
break;
default : type = 'Lecture hall'
}
Rooms.find({type : type},
(err, rooms) => {
if (!err){
console.log('retrieved rooms ' + rooms)
return rooms;
}
})
}
Here I have provided only two methods because full implementation is very long and I feel if I could understand how to apply synchronous way to one method, I can manage the rest of the methods. Can someone please help me?
Well yes await is only available inside an async function so put async infront of findSuitableRoom.
Also you did a classic mistake. You use return inside of a callback function, and expect getRooms to return you some value.
async function findSuitableRoom(
_lecturer,
_sessionDay,
_sessionTime,
_sessionDuration,
_sessionType
) {
let assignedRoom = selectRoomByLevel(
preferredRooms,
lecturer.level,
_sessionDuration
);
if (!assignedRoom.success) {
try {
let rooms = await getRooms(_sessionType);
} catch (err) {
console.log("no rooms found");
}
assignedRoom = assignRoom(
_lecturer.rooms,
_sessionDuration,
_lecturer.level
);
} else {
arr_RemovedSessions.push(assignedRoom.removed);
}
return assignedRoom;
}
Also wrap it in an try / catch
Since .find() returns an promise if you dont pass an callback you can write it like this
function getRooms(type) {
switch (type) {
case "Tutorial":
type = "Lecture hall";
break;
case "Practical":
type = "Lab";
break;
default:
type = "Lecture hall";
}
return Rooms.find({ type });
}
Note here findSuitableRoom is no longer synchronouse. Its async and returns an promise. That means you will need to use the function like this:
findSuitableRoom.then(res => { console.log(res); })
The 'await' operator can only be used in an 'async' function
This means whenever you want to use the await keyword it needs to be inside a function which has an async keyword (returns promise)
read this https://javascript.info/async-await for more info
const add = (a, b) => {
return new Promise((resolve, reject) => {
setTimeout(() => { //to make it asynchronous
if (a < 0 || b < 0) {
return reject("don't need negative");
}
resolve(a + b);
}, 2000);
});
};
const jatin = async () => {
try{
const sum = await add(10, 5);
const sum2 = await add(sum, -100);
const sum3 = await add(sum2, 1000);
return sum3;
} catch (e) {
console.log(e);
}
};
jatin()
Try this
let's understand this
add is a normal function that does some asynchronous action like
waiting for 2 seconds
normally we use await with async function so in order to use it we make as async function jatin and use await with add function call
to make it synchronous, so until first await add call() doesn't
happen it wont execute another await add call().
Example code if you will use in your app.js
router.post("/users/login", async (req, res) => {
try {
const user = await User.findByCredentials(
req.body.email,
req.body.password
);
const token = await user.generateToken();
res.status(200).send({
user,
token,
});
}
catch (error) {
console.log(error);
res.status(400).send();
}
});

How to create a re-usable caching method while using node-cache

I'm using node-cache on my Node.JS & Express web service. But after a while, I've got a bunch of similar logics cluttering, like these:
let data = this.cache.get('some-key')
if (data) {
return shopInfo
} else {
data = someModel.find(id)
this.cache.set('some-key', data)
return data
}
I Googled about it and found a possible solution here. But I don't wanna pass a callback function every time. Is there a better way? Or how can I modify it to utilize async/await instead of callback function?
get(key, storeFunction) {
const value = this.cache.get(key);
if (value) {
return Promise.resolve(value);
}
return storeFunction().then((result) => {
this.cache.set(key, result);
return result;
});
}
You're going to need a callback or promise as a parameter. Here's perhaps an easier way:
Helper function:
get(key, retrieveData) {
const value = this.cache.get(key);
if (value) {
return value;
}
const data = retrieveData()
this.cache.set(key, data);
return data;
}
Then you can use:
const result = get('some-key', () => someModel.find(id))
return result
Despite using a callback, it's still clean. No need to complicate your code with promises if you don't need them.
if you dont want to past callback every time, you can do opposite - modify callback itself.
// simple memoized...
function memoized(cache, fn) {
return async key => cache.has(key) ? cache.get(key) : cache.set(key, await fn(key)).get(key);
}
// some model handler...
function fetchFunction(key) {
console.log('call fetchFunction');
return Promise.resolve(key + '_value');
}
// modify callback...
// example: model.fetchFunction = memoized(new Map(), model.fetchFunction).bind(model);
fetchFunction = memoized(new Map(), fetchFunction);
// test...
(async () => {
console.log(await fetchFunction('test'));
console.log(await fetchFunction('test'));
})()

Awaiting for all the callbacks to return before HttpsCallableResult completes

I am using Firebase Functions with Unity. The main function returns before the database functions finish. I am still new to Node.js and I am still trying to get my head around all the Async Callback stuff.
I have tried CallAsync, ContinueWith, and Coroutines, but the function always continues after the first return (I use Task.isCompleted() to check for that).
My Node.js functions are something like this:
exports.my_fn = functions.https.onCall((data, context) => {
dbsessions.child(id).once("value").then(function(snapshot) {
if (snapshot.val()) {
Create();
} else {
Move(session);
}});
});
function Move(session) {
if (session["error"]) {
return return_stuff;
} else {
if (some_condition) {
dbsessions.child(id).set(sson, function(set_error) {
if (set_error) {
return return_stuff;
} else {
return return_stuff;
}
});
} else {
dbaimoves.child(stt).child(dif).once("value").then(function(snapshot) {
if (snapshot.val()) {
return return_stuff;
} else {
if (!first) {
dbsessions.child(id).set(sson, function(set_error) {
if (set_error) {
return return_stuff;
} else {
return return_stuff;
}
});
} else {
return return_stuff;
}
}
}, function(errorObject) {
if (errorObject) {
return return_stuff;
}
});
}}}
var Create = function(data, callback) {
dbdifficulty.child(data).once("value").then(function(snapshot) {
if (snapshot.val()) {
return callback();
} else {
dbsessions.child(data.id).set(data, function(set_error) {
if (set_error) {
return callback();
} else {
return callback();
}});
}});
}
(I skipped unnecessary data to keep the question simple). It is basically nested returns and database operations, callbacks and functions call each other.
My C# Unity code is something like this:
private async Task<string> AddMessageAsync(string text)
{
// Create the arguments of the callable function.
var data = new Dictionary<string, string>();
data["s"] = text;
data["d"] = "0";
var function = func.GetHttpsCallable("my_fn");
var Tfn = function.CallAsync(data);
var TRes = await Tfn;
if (Tfn.IsCompleted)
{
JFunc result = JsonUtility.FromJson<JFunc>(TRes.Data.ToString());
Debug.Log("error:" + result.error);
return result.move;
}
return "error";
}
The codes above resemble my actual code, which calls the function from Unity, the function runs on Firebase and returns shortly (before it goes into Create() or Move()), Unity receives the result (null). A few seconds later the function finishes successfully on Firebase, but Unity does not receive anything about that (or maybe it does, but I can't handle it properly).
I need to know:
how to make the main function return what the other functions return, and
how to make C# wait and keep listening to the returned values, instead of thinking the task has completed after the first return. It would be even better if I can only return only when the result is ready.
To make the Cloud Functions code return a value, make sure that each function returns a value or a promise. Promises "bubble up" meaning that the value you return from the most-nested code will be return to the top-level, as long as you have a return on each level.
So in your code from a quick scan, you need:
exports.my_fn = functions.https.onCall((data, context) => {
return dbsessions.child(id).once("value").then(function(snapshot) {
if (snapshot.val()) {
return Create();
} else {
return Move(session);
}});
});
var Create = function(data, callback) {
return dbdifficulty.child(data).once("value").then(function(snapshot) {
if (snapshot.val()) {
return callback();
} else {
return dbsessions.child(data.id).set(data, function(set_error) {
if (set_error) {
return callback();
} else {
return callback();
}});
}});
}
I've only instrumented the top-level my_fn and Create here, to show what to do. You'll have to do the same for Move yourself.

NodeJS callback variable assignation is always one update behind

I'm learning NodeJS and I have the following code:
var test = '';
function test2(name,callback) {
UserData
.findOne({'token': name})
.then(function(user) {
test = user.token;
console.log('current: '+user.token);
return callback(user.token);
})
.catch(function(err) {
console.log(err);
});
}
var isAuthenticated = function(req,res,next){
test2(req.cookies.remember_me, function(user) {test=user; });
console.log('test:::: '+test);
var isLog = false;
if(req.session.user!= undefined && req.session.user===test){
isLog=true;
}
if(req.cookies.remember_me ===test){
console.log('test'+test);
isLog=true;
}
if(isLog){
return 1;
}else
{
console.log('not auth');
return -1;
}
}
and the result is :
test:::: P9Ysq2oSCHy1RVyWsePxJhpEYLD81qOiIayTyiNJCnOkmllvEspwrDAW8tD9rmfJ
not auth
current: k8LJcCty6568QpXNS3urBedlJ0MDfEYlbOqo9Q7tQi9EOyeSkyesgHHzUjBhDgZx
I know it's bcause if the async nature of NodeJS but how can i make test to be always the same as 'current';
Thank you.
You are making a pretty classic mistake of expecting code to run in the order written, but it doesn't because of the async nature of javascript. For example:
test2(req.cookies.remember_me, function(user) {test=user; });
console.log('test:::: '+test);
Here your console.log() will run before the callback because the callback only happens after you've heard back from the DB. (Although it's not clear where that test value ('P9Ysq2oSCH...') is coming from.
As long as you're learning Node, you should start by trying to avoiding mixing callbacks and promises. Your findOne() function returns a promise, which is why you can call then() on it. You should just return this promise and then call then() in the calling function:
function test2(name) {
// return is important - you're returning the promise which you will use later.
return UserData.findOne({'token': name})
.then(function(user) {
return user.token;
})
.catch(function(err) {
console.log(err);
});
}
function isAuthenticated(req,res,next){
return test2(req.cookies.remember_me)
.then(function(token) {
console.log('test:::: '+token);
var isLog = false;
if(req.session.user!= undefined && req.session.user===token){
isLog=true;
}
if(req.cookies.remember_me ===token){
isLog=true;
}
return isLog
})
}
// Use the function
isAuthenticated(req,res,next)
.then(function(isLoggedin){
if(isLoggedin) {
// user logged in
} else {
// not logged in
}
})

Resources