Event listener Javascript - javascript

I have 3 instances of the class Foo(). Let's call them foo1, foo2 and foo3 .
By default, the propriety isReady of Foo() is false. In the constructor of Foo(), there is an asynchrousnous method that sets isReady to true. For instance, I could be setTimout with different duration. After I instantiate these 3 instance, there is function called: startMainAction() that should be called only after foo1.isReady = true and foo2.isReady = true and foo3.isReady = true
Given that the asynchronous function is undetermined, (it could be random duration), there is no way for sure the order at which the foo instances will have their propriety isReady set to true. Hence, I do not think I could use callbacks to call startMainAction .
How can that be achieved?
class Foo{
constructor(n){
this.isReady = false;
setTimeout(()=>{
this.isReady = true;
console.log('I am ready '+n);
}, Math.random()*1000)
}
}
const foo1 = new Foo(1)
const foo2 = new Foo(2)
const foo3 = new Foo(3)
function startMainAction(){
console.log('All fooes must be ready before I was called')
}

One option is to add a method which adds a callback to call once you set isReady to true:
onReady(callback) {
this.readyCallbacks.push(callback);
}
Then, with those three instances, call onReady with the resolve of a Promise, and call Promise.all on those three Promises:
const foos = [foo1, foo2, foo3];
Promise.all(foos.map(foo => new Promise(resolve => foo.onReady(resolve))))
.then(startMainAction);
class Foo {
constructor() {
this.readyCallbacks = [];
this.isReady = false;
setTimeout(() => {
this.isReady = true;
console.log('I am ready');
this.readyCallbacks.forEach(callback => callback());
}, Math.random() * 1500)
}
onReady(callback) {
this.readyCallbacks.push(callback);
}
}
const foo1 = new Foo()
const foo2 = new Foo()
const foo3 = new Foo()
function startMainAction() {
console.log('All fooes must be ready before I was called')
}
const foos = [foo1, foo2, foo3];
Promise.all(foos.map(foo => new Promise(resolve => foo.onReady(resolve))))
.then(startMainAction);
I suppose you could also pass the readyCallback in the constructor, which would result in less code, but that doesn't seem quite appropriate, because a Foo (say, foo4) may not need a readyCallback, and a readyCallback isn't necessary to create an instance, it's a handler that other parts of the code request to add to an instance. (for example, if you had two parts of the code which had to listen for the isReady of foo1, you would need something like this onReady method.)

Have the constructor method create a promise that will resolve when the condition to make the object ready happens.
Then get the promises from all of the instances and wrap them with Promise.all so they wait for all of them.
class Foo {
constructor() {
this.isReady = false;
this.isReadyPromise = new Promise(
res => setTimeout(
() => { console.log("I am ready"); this.isReady = true; res(true); },
1000 + 1000 * (Math.floor(Math.random() * Math.floor(3)))
)
)
}
}
const foo1 = new Foo(),
foo2 = new Foo(),
foo3 = new Foo();
Promise.all([ foo1.isReadyPromise, foo2.isReadyPromise, foo3.isReadyPromise ]).then(() => {
console.log("All 3 are ready")
});

You can add all of your instances of Foo into a list, an array or anything that you can iterate over. Then do something along the lines of
let foos = [foo1, foo2, foo3];
function WaitUntilReady(callback) {
let ready = true;
foreach (let foo of foos) {
if ( !foo.isReady ) {
ready = false;
break;
}
}
if ( ready ) {
callback();
} else {
setTimeout(WaitUntilReady, 100); //100 miliseconds until next check
}
}
WaitUntilReady(function() {
//every instance is ready
});
Edit: using Promises seems more fancy, not sure if it really is. I'm not that much of a javascript developer so i did not even think of using them

Related

Exporting a function in NodeJS, different attempts returned different errors: undefined, TypeError properties undefined, and promise pending [duplicate]

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

How to "append" an action to a function?

I wanted to create a function that I can add actions to later. I tried this:
Function.prototype.appendAction = function(action) {
let _ogFn = this
return function(...args) {
_ogFn(...args)
action()
}
}
function a() {
console.log("foo")
}
a()
a = a.appendAction(() => console.log("bar"))
a()
This does work, but how can I make it change the function automatically? I want to make it work like this:
a()
a.appendAction(() => console.log("bar")) //note it doesn't have the "a = "
a() //changed function
Depending on your scenario you could create a function decorator. This would mean defining a function (for example fnMod) that accepts a function fn and returns a decorated version that executes some before and after hooks.
const a = fnMod(() => {
console.log("foo");
});
a();
a.appendAction(() => console.log("bar"));
a();
const john = {
name: "John Doe",
age: 42,
greet: fnMod(function () {
console.log(`I'm ${this.name}.`); // <- uses `this` so use a normal function
})
}
john.greet.prependAction((name) => {
console.log(`Hi ${name}!`); // <- does not use `this` so both a normal function
}); // or arrow function will work fine
john.greet.appendAction(function () {
console.log(`I'm ${this.age} years old.`);
});
john.greet("Jane");
function fnMod(fn) {
const before = [];
const after = [];
function hookable(...args) {
before.forEach(fn => fn.apply(this, args));
const result = fn.apply(this, args);
after.forEach(fn => fn.apply(this, args));
return result;
};
hookable.prependAction = (fn) => {
before.unshift(fn);
}
hookable.appendAction = (fn) => {
after.push(fn);
}
return hookable;
}
You might want to tweak it to your liking. Currently both this and all arguments passed to the decorated function are forwarded to all hooks, which might or might not be desirable.
If you just want to call it immediately:
Function.prototype.appendAction = function(action) {
let _ogFn = this
return (function(...args) {
_ogFn(...args)
action()
})()
}
function a() {
console.log("foo")
}
a.appendAction(() => console.log("bar"))
That is the immediately invoked function expression (IIFE) pattern
For your purposes you could use eval:
First, grab the user input however you are handling it and parse it as a string.
Then use eval like so:
let userInput = 'console.log("hello world")';
const callUserFunction = (action) => {
eval(`function fn(){${action}}`);
fn();
}
callUserFunction(userInput);
Be aware that allowing a user to write or JS is a security risk, which can open doors to all kinds of attacks, so in most scenarios, I would discourage doing so

Does the asyc\await syntax resolve chained functions?

In this example, if the function was run, would both promises get resolved before foo is return ?
async function() {
var foo = await iReturnAPromise().iReturnAPromiseUnrelatedToMyParent();
return foo;
}
foo as it is, can only carry one resolution. You may do like
foo = await iReturnAPromise().then(v => (doSomeThingWith(v), iReturnAPromiseUnrelatedToMyParent()));
in which case foo will be assigned the resolution of iReturnAPromiseUnrelatedToMyParent. However if you would like to access both resolutions (which are independent of each other) then you may do like;
async function test(){
[foo,bar] = await Promise.all([Promise.resolve(10), Promise.resolve(20)]);
return [foo,bar];
}
test().then(([a,b]) => console.log(a,b));

Dynamically add argument to function in javascript

I'm testing my app, I have a dependency injection system that works like this (I paste here a very simplified version)
test.start = function(callback) {
// do stuff;
load(callback);
}
function load(callback) {
var args = ...// read callback's arguments
// args = ['moduleA', 'moduleB'];
var injectedArgs = args.map(function(a) {
return require('./lib/' + a);
});
// call function with deps injected
callback.apply(null, injectedArgs);
}
// test.js
test.start(function(moduleA, moduleB) {
moduleA.functionA();
moduleB.functionB();
});
What I need to do is an async job before calling the callback and start the test, in this particular case, that would be
test.start = function(callback) {
// do stuff;
load(function(moduleA, moduleB, moduleC) {
moduleC.resetData(function() {
return callback(moduleA, moduleB);
})
});
}
I need to make this dynamic, for all the test that have different modules as their callback arguments.
Requirements are
I don't want to change test definitions
I must call load only once per test
So basically I need to create the middle function that add moduleC argument, run the resetData function and call the original callback.
I can build the dynamically extended argument list using
var fn = new Function(newArgumentList, /*function body*/) but I can't attach the correct function body (using a string) because I lose the original callback context.
UPDATE
The DI library is modularity and it reads the callback definition to grab dependency. So basically, given the callback function(foo, bar) { ... } I need to create a new function(foo, bar, db) { db.resetData(function() { return callback(foo, bar) } )}
If load, itself, is something you can change (e.g., not part of the test definitions), you'll be glad to know this is quite easy to do: You use Function#apply:
function load(callback) {
var origCallback;
if (/*flag saying you need to do this*/) {
origCallback = callback;
callback = function() {
/*...do your injected work here...*/
return origCallback.apply(this, arguments); // `arguments` looks like pseudo-code, but it isn't; it's an identifier created in the function's scope (for non-arrow functions)
};
}
var args = ...// read callback's arguments
// args = ['moduleA', 'moduleB'];
var injectedArgs = args.map(function(a) {
return require('./lib/' + a);
});
// call function with deps injected
callback.apply(null, injectedArgs);
}
You can even do it if you can't change load, provided you can update the load symbol to make it point at a new function:
var oldload = load;
load = function() {
/*...do your reset work here...*/
return oldload.apply(this, arguments);
};
If you're curious, yes, you can update the symbols created by function declarations (and yes, that's per spec, not "tricky"). E.g., this is perfectly valid:
function foo() {
console.log("I'm foo");
}
var oldFoo = foo;
foo = function() {
console.log("I'm the new foo");
return oldFoo.apply(this, arguments);
};
foo(); // "I'm the new foo", then "I'm foo"
Example:
function foo() {
snippet.log("I'm foo");
}
var oldFoo = foo;
foo = function() {
snippet.log("I'm the new foo");
return oldFoo.apply(this, arguments);
};
foo(); // "I'm the new foo", then "I'm foo"
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
I found out the solution at the end.
Since modularity supports angular-like injection like this
['modules/a', 'modules/b', function(moduleA, moduleB) {
// use modules here
}]
I could write my custom callback like this
var customCallback = function() {
var args = Array.prototype.slice.call(arguments);
var injectedDBModule = args.pop();
injectedDBModule.resetData(function() {
// call the original callback function
return originalCallback.apply(null, args);
}
}
var loadCallback = originalInjectedModules; // ['moduleA', 'moduleB']
loadCallback.push('db');
loadCallback.push(customCallback);
modularity.load(loadCallback);

Can the object you create with a constructor function ever itself be a function?

I hope this question makes sense. Can I ever do something like the following:
function constructor_function() {...code...};
var a = new constructor_function();
a();
If a constructor returns an object, that will be the value of the new .. expression. A function is an object, so you do what you want:
function ConstructorFunction() {
return function () { alert("A function!"); };
}
var a = new ConstructorFunction();
a();
function Wrapper(constr, func) {
return function() {
var f = func();
constr.apply(f, arguments);
return f;
}
}
function ConstructorFunction() {
return function() {
console.log("A function");
}
}
function Constructor() {
this.foo = 42;
}
var MyFunction = Wrapper(Constructor, ConstructorFunction);
var o = MyFunction();
o(); // A function
console.log(o.foo); // 42
To both manipulate the this state as intended and have the object returned be a function is difficult to do without a lot of extra hoops to jump through.
This is about as easy I could make it. You have your standard Constructor function that manipulates this as an object. Then you have your ConstructorFunction which is the function you want the constructor to return. This must be a factor that returns a new function each time it's called.
You wrap the two together to get a function which returns an object which both has the returned function and the manipulation of state.
Live example
You can return a function from the constructor, but then it's not really a constructor. You can just use the function as a regular function, the new keyword is not needed:
function constructor_function() {
return function() { alert(42); }
};
var a = constructor_function();
a();
As a function is an object, you can even add properties to it:
function constructor_function() {
var t = function() { alert(42); }
t.size = 1337;
return t;
};
var a = constructor_function();
a();
alert(a.size);

Categories