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
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
});
I have a function foo
var foo = function(){
this.doRandomStuff = function(callback){
//do Something
callback(false);
}
}
var bar = function(){
var fooInstance = new foo();
fooInstance.doRandomStuff(function(val){
//do Something with val
})
}
I want to write test for bar function and I am using mocha and sinon for this.
describe("Foo Test",function(){
it("testing foo",function(done){
var instance = new foo();
sinon.stub(instance,'doRandomStuff').callsArgWith(0,true); // This Doesn't work
sinon.stub(foo,'doRandomStuff').callsArgWith(0,true); // This also Doesn't work
bar();
done();
})
});
I get below Exception :
TypeError: Cannot stub non-existent own property doRandomStuff
Alternative approach to make it more testable is module approach as below:
foo.js
function doRandomStuff(callback) {
callback(false);
}
module.exports = {
doRandomStuff
}
bar.js
const foo = require('./foo');
module.exports = function() {
foo.doRandomStuff(function(val) {
console.log('test val', val); // for testing purpose
})
}
test.js
const sinon = require('sinon');
const foo = require('./foo');
const bar = require('./bar');
describe('Foo Test', function() {
it("testing foo",function(done){
sinon.stub(foo, 'doRandomStuff').callsArgWith(0,true);
bar(); // output: "test val true"
done();
});
});
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
});
var foo = (function(){
var c = function(requests) {
bar();
};
c.prototype = {
bar: bar
};
return c;
})();
var f = new foo();
f.baz(function(){
console.log('new instance of foo created');
});
http://jsfiddle.net/LecJM/
I would like to create a callback function which is called when a new instance of the "class" foo is created. Is this possible? Obviously the code above won't compile, I just want to give you an idea of what I'm trying to achieve.
var Foo = function (createdCallback) {
createdCallback();
return this;
};
var bar = new Foo(function () {
console.log("instance created");
});
Is this what you want to achieve?
Something like this?
var foo = (function(){
var c = function(requests) {
// Initialize your instance
// ...
// Notify
notifyCreated(this);
};
c.prototype = { ... };
var createdCallbacks = [];
c.onCreate = function(callback) {
createdCallbacks.push(callback);
}
function notifyCreated(instance) {
// Note: forEach requires ES5 or a shim
// Just use whatever you want to loop over the array
createdCallbacks.forEach(function(callback) {
callback(instance);
});
}
return c;
})();
// Add callback *before* creating instances
foo.onCreate(function(instance){
console.log('new instance of foo created', instance);
});
// Create an instance
var f = new foo();
Basically, you add a method to foo (and not to foo.prototype) to add a callback. Inside your constructor, you call all registered callbacks (here demonstrated with a separate internal function). To use, you first register a callback and then start creating instances.
EDIT: As requested, with just one callback:
var foo = (function(){
var c = function(requests) {
// Initialize your instance
// ...
// Notify
notifyCreated(this);
};
c.prototype = { ... };
// Use a dummy callback by default
var notifyCreated = function(){};
c.onCreate = function(callback) {
notifyCreated = callback;
}
return c;
})();
Demo
EDIT 2: Heck, if you're only going to need one callback, you might as well get rid of the onCreate function and just expose the callback as a variable. There are a few downsides to this though:
You can't do input checking, for example you can't test if the callback is actually a function before storing it.
Others can trigger the callback externally through foo.onCreate(anInstance).
If those are not problematic (for example if you're not exposing foo anyway), feel free to use this extremely simple snippet:
var foo = (function(){
var c = function(requests) {
// Initialize your instance
// ...
// Trigger callback
c.onCreate(this);
};
c.prototype = { ... };
// Expose callback on "class"
c.onCreate = function(){};
return c;
})();
// Set callback *before* creating instances
foo.onCreate = function(instance){
console.log('new instance of foo created', instance);
};
// Create an instance
var f = new foo();
Demo
Try this
var foo = function() {
this.baz();
};
foo.prototype.baz = function () {
console.log('new instance of foo created');
};
var f = new foo();
How can I best handle a situation like the following?
I have a constructor that takes a while to complete.
var Element = function Element(name){
this.name = name;
this.nucleus = {};
this.load_nucleus(name); // This might take a second.
}
var oxygen = new Element('oxygen');
console.log(oxygen.nucleus); // Returns {}, because load_nucleus hasn't finished.
I see three options, each of which seem out of the ordinary.
One, add a callback to the constructor.
var Element = function Element(name, fn){
this.name = name;
this.nucleus = {};
this.load_nucleus(name, function(){
fn(); // Now continue.
});
}
Element.prototype.load_nucleus(name, fn){
fs.readFile(name+'.json', function(err, data) {
this.nucleus = JSON.parse(data);
fn();
});
}
var oxygen = new Element('oxygen', function(){
console.log(oxygen.nucleus);
});
Two, use EventEmitter to emit a 'loaded' event.
var Element = function Element(name){
this.name = name;
this.nucleus = {};
this.load_nucleus(name); // This might take a second.
}
Element.prototype.load_nucleus(name){
var self = this;
fs.readFile(name+'.json', function(err, data) {
self.nucleus = JSON.parse(data);
self.emit('loaded');
});
}
util.inherits(Element, events.EventEmitter);
var oxygen = new Element('oxygen');
oxygen.once('loaded', function(){
console.log(this.nucleus);
});
Or three, block the constructor.
var Element = function Element(name){
this.name = name;
this.nucleus = {};
this.load_nucleus(name); // This might take a second.
}
Element.prototype.load_nucleus(name, fn){
this.nucleus = JSON.parse(fs.readFileSync(name+'.json'));
}
var oxygen = new Element('oxygen');
console.log(oxygen.nucleus)
But I haven't seen any of this done before.
What other options do I have?
Update 2:
Here is an updated example using an asynchronous factory method. N.B. this requires Node 8 or Babel if run in a browser.
class Element {
constructor(nucleus){
this.nucleus = nucleus;
}
static async createElement(){
const nucleus = await this.loadNucleus();
return new Element(nucleus);
}
static async loadNucleus(){
// do something async here and return it
return 10;
}
}
async function main(){
const element = await Element.createElement();
// use your element
}
main();
Update:
The code below got upvoted a couple of times. However I find this approach using a static method much better:
https://stackoverflow.com/a/24686979/2124586
ES6 version using promises
class Element{
constructor(){
this.some_property = 5;
this.nucleus;
return new Promise((resolve) => {
this.load_nucleus().then((nucleus) => {
this.nucleus = nucleus;
resolve(this);
});
});
}
load_nucleus(){
return new Promise((resolve) => {
setTimeout(() => resolve(10), 1000)
});
}
}
//Usage
new Element().then(function(instance){
// do stuff with your instance
});
Given the necessity to avoid blocking in Node, the use of events or callbacks isn't so strange(1).
With a slight edit of Two, you could merge it with One:
var Element = function Element(name, fn){
this.name = name;
this.nucleus = {};
if (fn) this.on('loaded', fn);
this.load_nucleus(name); // This might take a second.
}
...
Though, like the fs.readFile in your example, the core Node APIs (at least) often follow the pattern of static functions that expose the instance when the data is ready:
var Element = function Element(name, nucleus) {
this.name = name;
this.nucleus = nucleus;
};
Element.create = function (name, fn) {
fs.readFile(name+'.json', function(err, data) {
var nucleus = err ? null : JSON.parse(data);
fn(err, new Element(name, nucleus));
});
};
Element.create('oxygen', function (err, elem) {
if (!err) {
console.log(elem.name, elem.nucleus);
}
});
(1) It shouldn't take very long to read a JSON file. If it is, perhaps a change in storage system is in order for the data.
I have developed an async constructor:
function Myclass(){
return (async () => {
... code here ...
return this;
})();
}
(async function() {
let s=await new Myclass();
console.log("s",s)
})();
async returns a promise
arrow functions pass 'this' as is
it is possible to return something else when doing new (you still get a new empty object in this variable. if you call the function without new. you get the original this. like maybe window or global or its holding object).
it is possible to return the return value of called async function using await.
to use await in normal code, need to wrap the calls with an async anonymous function, that is called instantly. (the called function returns promise and code continues)
my 1st iteration was:
maybe just add a callback
call an anonymous async function,
then call the callback.
function Myclass(cb){
var asynccode=(async () => {
await this.something1();
console.log(this.result)
})();
if(cb)
asynccode.then(cb.bind(this))
}
my 2nd iteration was:
let's try with a promise instead of a callback.
I thought to myself: strange a promise returning a promise, and it worked. .. so the next version is just a promise.
function Myclass(){
this.result=false;
var asynccode=(async () => {
await new Promise (resolve => setTimeout (()=>{this.result="ok";resolve()}, 1000))
console.log(this.result)
return this;
})();
return asynccode;
}
(async function() {
let s=await new Myclass();
console.log("s",s)
})();
callback-based for old javascript
function Myclass(cb){
var that=this;
var cb_wrap=function(data){that.data=data;cb(that)}
getdata(cb_wrap)
}
new Myclass(function(s){
});
One thing you could do is preload all the nuclei (maybe inefficient; I don't know how much data it is). The other, which I would recommend if preloading is not an option, would involve a callback with a cache to save loaded nuclei. Here is that approach:
Element.nuclei = {};
Element.prototype.load_nucleus = function(name, fn){
if ( name in Element.nuclei ) {
this.nucleus = Element.nuclei[name];
return fn();
}
fs.readFile(name+'.json', function(err, data) {
this.nucleus = Element.nuclei[name] = JSON.parse(data);
fn();
});
}
This is a bad code design.
The main problem is in the callback your instance it's not still execute the "return", this is what I mean
var MyClass = function(cb) {
doAsync(function(err) {
cb(err)
}
return {
method1: function() { },
method2: function() { }
}
}
var _my = new MyClass(function(err) {
console.log('instance', _my) // < _my is still undefined
// _my.method1() can't run any methods from _my instance
})
_my.method1() // < it run the function, but it's not yet inited
So, the good code design is to explicitly call the "init" method (or in your case "load_nucleus") after instanced the class
var MyClass = function() {
return {
init: function(cb) {
doAsync(function(err) {
cb(err)
}
},
method1: function() { },
method2: function() { }
}
}
var _my = new MyClass()
_my.init(function(err) {
if(err) {
console.error('init error', err)
return
}
console.log('inited')
// _my.method1()
})
I extract out the async portions into a fluent method. By convention I call them together.
class FooBar {
constructor() {
this.foo = "foo";
}
async create() {
this.bar = await bar();
return this;
}
}
async function bar() {
return "bar";
}
async function main() {
const foobar = await new FooBar().create(); // two-part constructor
console.log(foobar.foo, foobar.bar);
}
main(); // foo bar
I tried a static factory approach wrapping new FooBar(), e.g. FooBar.create(), but it didn't play well with inheritance. If you extend FooBar into FooBarChild, FooBarChild.create() will still return a FooBar. Whereas with my approach new FooBarChild().create() will return a FooBarChild and it's easy to setup an inheritance chain with create().
You can run constructor function with async functions synchronously via nsynjs. Here is an example to illustrate:
index.js (main app logic):
var nsynjs = require('nsynjs');
var modules = {
MyObject: require('./MyObject')
};
function synchronousApp(modules) {
try {
var myObjectInstance1 = new modules.MyObject('data1.json');
var myObjectInstance2 = new modules.MyObject('data2.json');
console.log(myObjectInstance1.getData());
console.log(myObjectInstance2.getData());
}
catch (e) {
console.log("Error",e);
}
}
nsynjs.run(synchronousApp,null,modules,function () {
console.log('done');
});
MyObject.js (class definition with slow constructor):
var nsynjs = require('nsynjs');
var synchronousCode = function (wrappers) {
var config;
// constructor of MyObject
var MyObject = function(fileName) {
this.data = JSON.parse(wrappers.readFile(nsynjsCtx, fileName).data);
};
MyObject.prototype.getData = function () {
return this.data;
};
return MyObject;
};
var wrappers = require('./wrappers');
nsynjs.run(synchronousCode,{},wrappers,function (m) {
module.exports = m;
});
wrappers.js (nsynjs-aware wrapper around slow functions with callbacks):
var fs=require('fs');
exports.readFile = function (ctx,name) {
var res={};
fs.readFile( name, "utf8", function( error , configText ){
if( error ) res.error = error;
res.data = configText;
ctx.resume(error);
} );
return res;
};
exports.readFile.nsynjsHasCallback = true;
Full set of files for this example could be found here: https://github.com/amaksr/nsynjs/tree/master/examples/node-async-constructor