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.
Related
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.
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];
});
}
I've developed a way to make two separate services to comunicate using a pub/sub channel using Redis, this is the main part of the code:
var Intercom = (function () {
var _event = new events.EventEmitter();
listener.subscribe("intercom");
listener.on("message", function(channel, message) {
try {
var data = JSON.parse(message);
_event.emit(data.controller, data.payload);
}
catch (e) {}
});
return {
on: function (evt, callback) {
_event.on(evt, callback);
},
emit: function (controller, payload) {
try {
sender.publish("intercom", JSON.stringify({ controller: controller, payload: payload}));
}
catch (e) {}
}
}
})();
Im using it on the main app just by: intercom.on('hook', hookController.main);
As you can see, if the query is "hook" the main() function of hookController is called. Its a very ExpressJs like approach.
The hookController does a very simple thing:
exports.main = function(req) {
console.log(req);
}
It is not very clear to my how the parameter "req" is getting passed to main(), and probably because of my lack of understanding about it, I cant figure it out how to pass another parameter to main from the main app, something like:
var foo = 'bar';
intercom.on('hook', hookController.main, foo);
I found a way to do it;
var foo = 'bar';
intercom.on('hook', function(req) { hookController.main(req, foo) });
Its a little bit ugly.
I have the following code:
module.exports.functionA = function(str) {
console.log(str);
}
In the same module, how do I call functionA? In other languages such as PHP, you can call another member of the same class using $this->functionA();
This does not work:
module.exports.functionA('Hello world!');
When functionA was assigned to module.exports it was still undefined. Instead do:
var functionA = function(str) {
console.log(str);
}
module.exports = {
functionA: functionA
}
Then the following will work:
module.exports.functionB = function() {
functionA('Hello world!');
}
I was wondering what the best approach is for configuring a module export. "async.function" in the example below could be a FS or HTTP request, simplified for the sake of the example:
Here's example code (asynmodule.js):
var foo = "bar"
async.function(function(response) {
foo = "foobar";
// module.exports = foo; // having the export here breaks the app: foo is always undefined.
});
// having the export here results in working code, but without the variable being set.
module.exports = foo;
How can I export the module only once the async callback has been executed?
edit
a quick note on my actual use-case: I'm writing a module to configure nconf (https://github.com/flatiron/nconf) in an fs.exists() callback (i.e. it will parse a config file and set up nconf).
Your export can't work because it is outside the function while the foodeclaration is inside. But if you put the export inside, when you use your module you can't be sure the export was defined.
The best way to work with an ansync system is to use callback. You need to export a callback assignation method to get the callback, and call it on the async execution.
Example:
var foo, callback;
async.function(function(response) {
foo = "foobar";
if( typeof callback == 'function' ){
callback(foo);
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback = cb;
}
}
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
Multiple callback way
If your module need to be called more than once you need to manage an array of callback:
var foo, callbackList = [];
async.function(function(response) {
foo = "foobar";
// You can use all other form of array walk.
for(var i = 0; i < callbackList.length; i++){
callbackList[i](foo)
}
});
module.exports = function(cb){
if(typeof foo != 'undefined'){
cb(foo); // If foo is already define, I don't wait.
} else {
callback.push(cb);
}
}
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js');
fooMod(function(foo){
//Here code using foo;
});
Promise way
You can also use Promise to solve that. This method support multiple call by the design of the Promise:
var foo, callback;
module.exports = new Promise(function(resolve, reject){
async.function(function(response) {
foo = "foobar"
resolve(foo);
});
});
Here async.function is just a placeholder to symbolise an async call.
In main
var fooMod = require('./foo.js').then(function(foo){
//Here code using foo;
});
See Promise documentation
An ES7 approach would be an immediatly invoked async function in module.exports :
module.exports = (async function(){
//some async initiallizers
//e.g. await the db module that has the same structure like this
var db = await require("./db");
var foo = "bar";
//resolve the export promise
return {
foo
};
})()
This can be required with await later:
(async function(){
var foo = await require("./theuppercode");
console.log(foo);
})();
ES6 answer using promises:
const asyncFunc = () => {
return new Promise((resolve, reject) => {
// Where someAsyncFunction takes a callback, i.e. api call
someAsyncFunction(data => {
resolve(data)
})
})
}
export default asyncFunc
...
import asyncFunc from './asyncFunc'
asyncFunc().then(data => { console.log(data) })
Or you could return the Promise itself directly:
const p = new Promise(...)
export default p
...
import p from './asyncModule'
p.then(...)
Another approach would be wrapping the variable inside an object.
var Wrapper = function(){
this.foo = "bar";
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
});
}
module.exports = new Wrapper();
If the initializer has error, at least you still get the uninitialized value instead of hanging callback.
You can also make use of Promises:
some-async-module.js
module.exports = new Promise((resolve, reject) => {
setTimeout(resolve.bind(null, 'someValueToBeReturned'), 2000);
});
main.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// outputs 'someValueToBeReturned' after 2 seconds
The same can happen in a different module and will also resolve as expected:
in-some-other-module.js
var asyncModule = require('./some-async-module');
asyncModule.then(promisedResult => console.log(promisedResult));
// also outputs 'someValueToBeReturned' after 2 seconds
Note that the promise object is created once then it's cached by node. Each require('./some-async-module') will return the same object instance (promise instance in this case).
Other answers seemed to be partial answers and didn't work for me. This seems to be somewhat complete:
some-module.js
var Wrapper = function(){
this.callbacks = [];
this.foo = null;
this.init();
};
Wrapper.prototype.init = function(){
var wrapper = this;
async.function(function(response) {
wrapper.foo = "foobar";
this.callbacks.forEach(function(callback){
callback(null, wrapper.foo);
});
});
}
Wrapper.prototype.get = function(cb) {
if(typeof cb !== 'function') {
return this.connection; // this could be null so probably just throw
}
if(this.foo) {
return cb(null, this.foo);
}
this.callbacks.push(cb);
}
module.exports = new Wrapper();
main.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined
});
main2.js
var wrapper = require('./some-module');
wrapper.get(function(foo){
// foo will always be defined in another script
});