Asynchronous nodejs module exports - node.js

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
});

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.

Async.queue crashes after processing 3 initial elements of the queue with concurrency = 3

Async.queue() intially runs as expected but crashes after processing the first N elements (N = 3).
When adding callback() after running getAddress(), concurrency is totally ignored. Subsequently getAddress() runs for all tasks passed to the queue via the stream.
The problem arose when attempting to build upon this tutorial.
Trying to determine the root cause and a solution. Seems possible that this is related to promise chaining?
Have attempted to refactor async.queue() following the async docs, but appears that the syntax is out of date and can't find a working example with chained promises.
const { csvFormat } = require('d3-dsv');
const Nightmare = require('nightmare');
const { readFileSync, writeFileSync } = require('fs');
const numbers = readFileSync('./tesco-title-numbers.csv',
{encoding: 'utf8'}).trim().split('\n');
const START = 'https://eservices.landregistry.gov.uk/wps/portal/Property_Search';
var async = require("async")
console.log(numbers)
// create a read stream
var ArrayStream = require('arraystream')
var stream = ArrayStream.create(numbers)
// set concurrency
N = 3
var q = async.queue(function (task, callback) {
let data = getAddress(task)
// , function(){
// callback();
},
// },
N);
q.drain = function() {
stream.resume()
console.log('all items have been processed');
resolve()
}
// or await the end
// await q.drain()
q.saturated = function() {
stream.pause();
}
// assign an error callback
q.error = function(err, task) {
console.error('task experienced an error');
}
stream.on("data", function(data) {
// console.log(data);
q.push(data)
})
var getAddress = async id => {console.log(`Now checking ${id}`);
const nightmare = new Nightmare({ show: true });
// Go to initial start page, navigate to Detail search
try {
await nightmare
.goto(START)
.wait('.bodylinkcopy:first-child')
.click('.bodylinkcopy:first-child');
} catch(e) {
console.error(e);
}
// Type the title number into the appropriate box; click submit
try {
let SOMEGLOBALVAR;
await nightmare
// does some work
} catch(e) {
console.error(e);
return undefined;
}
};
Determined the cause of the problem. The callback along with getAddressed needs to be returned.
let dataArray = []
N = 4
var q = async.queue(async function (task, callback) {
return getAddress(task).then((response)=>{
console.log(response);
dataArray.push(response);
callback()})
} ,
N);

How to spyOn NodeJS module singleton with Jasmine

I want to use Jasmine to spyOn a module which exports a method directly:
myModule
module.exports = (arg, arg) => {
//do something...
callAMethod();
//return
}
Jasmine
spyOn(myModule);
// since I don't have any method to use spyOn(myModule, <method>)
I found I could use this:
//describe..
var myModule = require('./myModule');
it...
myModule = jasmine.createSpy().and.callFake(function() {
return false;
}); // <-this should work
functionBeingTested(..args);
expect(myModule).toHaveBeenCalled(); /*I get false, even if
it is actually called
in the functionBeingTested*/
Another solution i found was to spyOn myModule.prototype or set jasmine.createSpy().and.returnValue(false) but I have no success with both either.
How can I use spyOn like I said above?
I've created a simple snippet which mimics the require js
functionality to load the library and call the method.
I've listed three spies each with a different scenario explaining how
to spy
Note:
I've not used the require.js in itself but mimicked its functionality
however
Because the require.js exports the module.exports, we have no
control over the functionality of the require call and hence we might
not be able to install a spy on that method.
Please follow the each it case to for the description.
Hope this helps!
var callAMethod = function() {
return "Hello";
}
var myModule = {};
myModule.exports = function(name, title) {
return callAMethod() + " " + title + "." + name;
}
// Note that this require mimics the actual require.js require methodology, bascially returns the instantiated version of the library to be used.
var require = function() {
return myModule.exports;
}
// some function that uses the require library to load the js and then does something with it.
var testFunc = function() {
var myMod = require('abc');
return myMod("Bruce", "Mr");
}
describe('test mymodule', function() {
// This test fails because though you are mocking the instance here and not the actual call.
it('test that fails', function() {
var myModuleTest = require('abc');
myModuleTest = jasmine.createSpy().and.callFake(function() {
return false;
});
var result = testFunc();
console.log(result);
expect(result).toBe(false);
});
// this test passes since we have hijacked the require attribute itself and then routed its call to return the Boolean true which means that myModule now return the true statement
it('test that passes', function() {
require = jasmine.createSpy().and.callFake(function() {
return function() {
return true;
};
});
var result = testFunc();
console.log(result);
expect(result).toBe(true);
});
//callAMethod method call from module.exports may not tested since its the require js that does this call and you have no hook into it, however you can directly test the method like this
it('test callAMethod', function() {
console.log(myModule.exports("Bruce", "Mr"));
spyOn(myModule, 'exports').and.returnValue("Hello Mr.Wayne!");
var testVal = myModule.exports("Bruce", "Mr");
expect(testVal).toEqual("Hello Mr.Wayne!");
})
});

How can I mock a function/property of a node module exported as a function using sinon?

I have a service module that is exported as a function. I need to pass a couple of things into it, like a configuration object so it does need to retain this structure. I am trying to stub out a function from the service but can't figure it out. In my app, I have a function that makes an API call that is problematic during testing so I'd like to stub it. (I understand I'd have to write my test differently to handle the async issue)
// myService.js
module.exports = function(config) {
function foo() {
returns 'bar';
}
return {
foo: foo
};
};
// test.js
var config = require('../../config');
var request = require('supertest');
var chai = require('chai');
var expect = chai.expect;
var sinon = require('sinon');
var myService = require('./myService.js')(config);
describe('Simple test', function(done) {
it('should expect "something else", function(done) {
var stub = sinon.stub(myService, 'foo').returns('something else');
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
stub.restore();
})
.end(done);
});
});
* /testRoute I set up as a simple GET route that simply returns the value from myService.foo()
The above is not working, and I believe it has to do with the way my service is exporting. If I write the service as below, the stub works fine.
module.exports = {
test: function() {
return 'something';
}
};
But again, I need to be able to pass in information to the module so I would like to keep my modules in the original structure above. Is there a way to stub a function from a module that exports in that manner? I was also looking into proxyquire but not sure if that is the answer.
The reason why your test stub does not work is that the foo function is created every time the module initializer is called. As you discovered, when you have a static method on the module, then you are able to stub.
There are a variety of solutions to this problem-- but the simplest is to expose the method statically.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function foo() {
return 'bar'
}
It's ugly, but works.
What if the foo function has a closure to variables within the service (which is why it lives within the service initializer). Then unfortunately these need to be explicitly passed in.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function(config) {
return function foo() {
return config.bar;
}
}
Now you can safely stub the module.
However, how you are stubbing should be considered unsafe. Only if your test works perfectly does the stub get cleaned up. You should always stub within the before and after (or beforeEach and afterEach) fixtures, such as:
// We are not configuring the module, so the call with config is not needed
var myService = require('./myService.js');
describe('Simple test', function(done) {
beforeEach(function () {
// First example, above
this.myStub = sinon.stub(myService, foo).returns('something else');
// Second example, above
this.myStub = sinon.stub(myService, foo).returns(function () {
returns 'something else';
});
});
afterEach(function () {
this.myStub.restore();
});
it('should expect "something else", function(done) {
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
})
.end(done);
});
});
There are other options to be able to stub dependencies using dependency injection. I recommend you look at https://github.com/vkarpov15/wagner-core or my own https://github.com/CaptEmulation/service-builder

How to get values from a promise with node.js without .then function

I have a problem with a promise using node.js. My code is below:
var p1 = new Promise(function(resolve, reject) {
// my function here
});
p1.then(function(result){
// my result
});
This code works but to get values from p1 I must use the .then method and my result values can be accessed just on p1.then. How do I access p1 values without .then?
Below are my expected results:
var p1 = new Promise(function(resolve, reject) {
// my function here
});
var abc = NextFunction(p1);
The p1 values will be used afterwards in code outside of the p1 variable.
p1 is a Promise, you have to wait for it to evaluate and use the values as are required by Promise.
You can read here: http://www.html5rocks.com/en/tutorials/es6/promises/
Although the result is available only inside the resolved function, you can extend it using a simple variable
var res;
p1.then(function(result){
res = result; // Now you can use res everywhere
});
But be mindful to use res only after the promise resolved, if you depend on that value, call the next function from inside the .then like this:
var res;
p1.then(function(result){
var abc = NextFunction(result);
});
You can use await after the promise is resolved or rejected.
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
var x = await resolveAfter2Seconds(10);
console.log(x); // 10
}
f1();
Be aware await expression must be inside async function though.
You can do this, using the deasync module
var node = require("deasync");
// Wait for a promise without using the await
function wait(promise) {
var done = 0;
var result = null;
promise.then(
// on value
function (value) {
done = 1;
result = value;
return (value);
},
// on exception
function (reason) {
done = 1;
throw reason;
}
);
while (!done)
node.runLoopOnce();
return (result);
}
function test() {
var task = new Promise((resolve, reject)=>{
setTimeout(resolve, 2000, 'Hello');
//resolve('immediately');
});
console.log("wait ...");
var result = wait(task);
console.log("wait ...done", result);
}
In nodejs 14.8.0+, you are able to use top level awaits.
So your script will now look like:
var p1 = await new Promise(function(resolve, reject) {
// my function here
});
// p1 is now the result of the promise, not a promise
If you are doing this in the REPL, it is not enabled by default, so you have to run the REPL with the --experimental-repl-await flag. Using nvm, I was able to test even node version 12.20.1 and the repl flag works.

Resources