I am trying to create a structure where i can stack multiple classes that extend the same parent class. by stacking I mean something like stacking then in the following example :
promise.then(
data=>{
return promise;
}
).then(
data=>{
return promise;
}
).then(
....
)
where the second datais the one resolved by the first returned promise.
for my case I have an operation class which have multiple child classes createUser, sendEmail and generateConfig. and I want to be able to stack those the following way :
let generateConfigREturnedValue = createUser(null, usercreationInput)
.sendEmail(createUSerReturnedValue, sendEmailInput)
.generateConfig(sendEmailReturnedValue, generateConfigInput)
.finish();
I want to be able to stack, the above way, any class that extends the operation parent class.
Note : The ReturnedValue arguments are not passed explicitly that way but rather that is how they will be passed and received by the nextInline operation.
EDIT:
So the parent operation class would have an abstract method called execute which will be automatically called wen chaining the child classes ( the child class will have its main code in that method ). So technically the class chaining I am talking about is chaining of the execute methods of each class while passing the arguments.
My thoughts :
Adding all the child operations to the prototype of each of the child operations, thus all child operations will have each other to call. After that I need to setup the, under the hood, argument passing from an operation to an other.
extending the prototype attribute lookup on the parent operation class to include fetching objects using their names from a dependency container ( something like awilix )
is what I want to do is implementable and which is the best thought of the couple above ?
First of all, if your functions perform asynchronous actions in order to get a result, you cannot expect your .finish() call to return that final result synchronously. The best you can hope for is a promise from .finish().
I would suggest to do all the hard work in the finish method, and let all other methods just collect the input data.
I assume you have the core asynchronous functions already available, and I will refer to them via the api object:
class Chain {
createUser(userCreationInput) {
this.userCreationInput = userCreationInput;
return this;
}
sendEmail: function(sendEmailInput) {
this.sendEmailInput = sendEmailInput;
return this;
}
generateConfig(generateConfigInput) {
this.generateConfigInput = generateConfigInput;
return this;
}
async finish() {
let createUserReturnedValue = await api.createUser(null, this.userCreationInput);
let sendEmailReturnedValue = await api.sendEmail(createUserReturnedValue, this.sendEmailInput);
return api.generateConfig(sendEmailReturnedValue, this.generateConfigInput);
}
}
let generateConfigReturnedValue = await new Chain()
.createUser(usercreationInput)
.sendEmail(sendEmailInput)
.generateConfig(generateConfigInput)
.finish();
Obviously the await should be used in an async function context.
This is just focussing on core functionality. You would have to add checks that all information is provided when finish is called, deal with error handling, ...etc.
If you don't like the new Chain() part in the expression, then you could define these three methods also as global functions and let those return a new instance of Chain. It's a choice between littering the global namespace or having the extra new Chain().
A middle ground would be to define the functions also as static methods -- again, returning a new instance of Chain. Then the expression would be:
let x = Chain.createUser(usercreationInput). // ...etc
Related
I read this implementation of an event emitter on LeetCode and wanted to ask a few questions.
What purpose does it serve to have the release method in the return of the subscribe method? Why can't I make it its own method?
How do I use the unsubscribe method like this and how would I use it if it was its own method?
The author said the reason behind putting the callback inside an object was to be able to add multiple callbacks of the same name. Is that considered good practice?
Any recommendations as to how to make this implementation better (readability, structure)?
Why isn't subscriptions variable defined in a constructor?
Thank you.
class EventEmitter {
subscriptions = new Map()
subscribe(eventName, callback) {
if (!this.subscriptions.has(eventName)) {
this.subscriptions.set(eventName, new Set())
}
const newSub = { callback }
this.subscriptions.get(eventName).add(newSub)
return {
unsubscribe: () => {
const evSub = this.subscriptions.get(eventName)
evSub.delete(newSub)
if (evSub.size === 0)
this.subscriptions.delete(eventName)
}
}
}
emit(eventName, ...args) {
const callbacks = this.subscriptions.get(eventName)
if (!callbacks) return
for (let c of callbacks) {
c.callback(...args)
}
}
}
What purpose does it serve to have the release method in the return of the subscribe method? Why can't I make it its own method?
Answer 1: In case unsubscribing needs information only available from making the subscription (like a subscription eventName) it makes sense to provide the function directly from the creation. It saves you having to store data needed in the unsubscription process in some intermediary form. You can make it is own method if you want, but still need to return it as in this code:
unsubscribe(eventName) => {
const evSub = this.subscriptions.get(eventName)
evSub.delete(newSub)
if (evSub.size === 0)
this.subscriptions.delete(eventName)
}
subscribe(eventName, callback) {
if (!this.subscriptions.has(eventName)) {
this.subscriptions.set(eventName, new Set())
}
const newSub = { callback }
this.subscriptions.get(eventName).add(newSub)
return () => unsubscribe(eventName);
}
How do I use the unsubscribe method like this and how would I use it if it was its own method?
Answer 2: You store the return value from subscribe some place you can access it, then invoke it if needed by calling unsubscribe(). How it's used is not different whether it's in its own function or not. It still needs to know the eventName so you need the one returned from subscribe. Like in this code:
const unsub = subscribe("event1", () => {});
// then later
unsub(); // unsubscribe from event1
The author said the reason behind putting the callback inside an object was to be able to add multiple callbacks of the same name. Is that considered good practice?
Answer 3: There's nothing wrong with it. It's a design choice.
Any recommendations as to how to make this implementation better (readability, structure)?
Answer 4: That's personal choice.
Why isn't subscription defined in a constructor?
Answer 5: I assume you're asking about subscriptions variable (plural) and not the subscription function. It's defined as a class variable which doesn't need any initialization specific to a ctor parameter so there's no need to make one. You could put it in a ctor if you wanted to but it just makes the code longer without any real benefit. If the ctor took in some parameters that would affect the initial value of subscriptions then it could be done in the ctor.
I'm making a UI and came across something that made me wonder. I made a general re-usable function that fetches data and returns it in a callback, which is given to the function by whatever is calling that function. But that's all it does, it fetches data and passes it onward. At the moment the function can take up to ~15 different parameters/props.
I made it a React Component at first, due to the feasibility of calling the function like so:
<SomeFunction
param1={some_param_1}
param2={some_param_2}
...
/>
This way I can easily add and omit parameters at will. However, the SomeFunction always returns null, as its main point is returning fetched data in a callback. Should this Component be reverted to a simple function without any React in it? If so, what is the best way to approach the parameters?
My mind can quickly come up with two alternatives, the first one being positional arguments:
function someFunction(param1, param2, ... param15)
But this seems like a stretch, as I need to give many nulls or such if I want to pass something as the 15th parameter.
Another way that came to mind is to use an object:
function someFunction(options)
and then access parameters like options.param1 and options.param2.
Is the Component approach or the function approach better in this type of case? And what is the best way to handle gazillion optional parameters to a function in JS? I'm not a total noob but it feels like there are so many ways to approach things and best practices in the JS world, not to mention the ever-changing nature of the language and its derivatives.
Two suggestions:
Make it a normal function that accepts its parameters as an object, probably using destructuring. (A component receives its props as an object, so that's basically the same thing.)
Return a promise rather than passing in a callback. Promises provide standard semantics that can be consumed with await in an async function and/or combined with the various promise combinators (Promise.all, Promise.race, etc.).
So for instance, if your function currently uses something that provides a promise (like fetch):
async function fetchTheInformation({param1, param2, param3 = "default for param3"}) {
const response = await fetch(/*...*/);
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
return response.appropriateMethodHere(); // .text(), .json(), .arrayBuffer(), etc.
}
if it doesn't use something that provides a promise:
function fetchTheInformation({param1, param2, param3 = "default for param3"}) {
return new Promise((resolve, reject) => {
// ...start the operation and call `resolve` if it works, `reject` with an `Error` if it doesn't...
});
}
In either case, the call to it can look like this in an async function:
const information = await fetchTheInformation({
param1: "parameter 1",
param2, // <=== If you happen to have `param2` in a variable/constant with the same name, no need to repeat it
});
(errors [rejections] will automatically propagate to the caller of the async function to be handled there)
or in a non-async function:
fetchTheInformation({
param1: "parameter 1",
param2, // <=== If you happen to have `param2` in a variable/constant with the same name, no need to repeat it, this is just like `param2: param,`
})
.then(information => {
// ...use the information...
})
.catch(error => { // Or return the promise chain to something else that will handle errors
// ...handle/report error...
});
About the parameter list: I assume at least one parameter is required, but if they're all optional (have reasonable defaults), you can do the list like this:
function example({a = "default for a", b = "default for b", c = "default for c"} = {}) {
}
The expressions after = within the destructuring provide defaults for those destructured parameters. The = {} at the end makes the entire parameters object optional, you with the above you can do example() or example({}) or example({a: "something"}), etc.
i write this code but i don`t now why my console.log give me undefined and how can i get value of prototype properties or functions
(function() {
function Users() {}
Users.prototype.getUser = function() {
$.getJSON('/usersList/users.json', function(request) {
Users.prototype.allUsers = request.users;
});
}
var users = new Users();
users.getUsers();
console.log(users.allUsers);
}
())
What i wont to achieve is to have this user list as my object property like User.allUsers in some array.
Thanks
This is because $.getJSON is asynchronous.
When you create an object from User prototype, $.getJSON has to call its callback yet.
In the other hand, you're trying to simulate global variables adding data properties to a given prototype. This is a bad practice and, at the end of the day, you've already realized that you got stuck on this approach.
What you need an initialization code executed whenever the site or app is started so those allUsers are called once and injected everywhere:
const initApp = () => Promise.all([
$.getJSON("/someData/users.json")
// other comma-separated async functions which return promises
])
initApp().then(([allUsers, otherData, yetAnotherData]) => {
callSomeFunctionWhichRequiresAllUsers(allUsers)
})
I'm trying to create a constructor for a blogging platform and it has many async operations going on inside. These range from grabbing the posts from directories, parsing them, sending them through template engines, etc.
So my question is, would it be unwise to have my constructor function return a promise instead of an object of the function they called new against.
For instance:
var engine = new Engine({path: '/path/to/posts'}).then(function (eng) {
// allow user to interact with the newly created engine object inside 'then'
engine.showPostsOnOnePage();
});
Now, the user may also not supply a supplement Promise chain link:
var engine = new Engine({path: '/path/to/posts'});
// ERROR
// engine will not be available as an Engine object here
This could pose a problem as the user may be confused why engine is not available after construction.
The reason to use a Promise in the constructor makes sense. I want the entire blog to be functioning after the construction phase. However, it seems like a smell almost to not have access to the object immediately after calling new.
I have debated using something along the lines of engine.start().then() or engine.init() which would return the Promise instead. But those also seem smelly.
Edit: This is in a Node.js project.
Yes, it is a bad practise. A constructor should return an instance of its class, nothing else. It would mess up the new operator and inheritance otherwise.
Moreover, a constructor should only create and initialize a new instance. It should set up data structures and all instance-specific properties, but not execute any tasks. It should be a pure function without side effects if possible, with all the benefits that has.
What if I want to execute things from my constructor?
That should go in a method of your class. You want to mutate global state? Then call that procedure explicitly, not as a side effect of generating an object. This call can go right after the instantiation:
var engine = new Engine()
engine.displayPosts();
If that task is asynchronous, you can now easily return a promise for its results from the method, to easily wait until it is finished.
I would however not recommend this pattern when the method (asynchronously) mutates the instance and other methods depend on that, as that would lead to them being required to wait (become async even if they're actually synchronous) and you'd quickly have some internal queue management going on. Do not code instances to exist but be actually unusable.
What if I want to load data into my instance asynchronously?
Ask yourself: Do you actually need the instance without the data? Could you use it somehow?
If the answer to that is No, then you should not create it before you have the data. Make the data ifself a parameter to your constructor, instead of telling the constructor how to fetch the data (or passing a promise for the data).
Then, use a static method to load the data, from which you return a promise. Then chain a call that wraps the data in a new instance on that:
Engine.load({path: '/path/to/posts'}).then(function(posts) {
new Engine(posts).displayPosts();
});
This allows much greater flexibility in the ways to acquire the data, and simplifies the constructor a lot. Similarly, you might write static factory functions that return promises for Engine instances:
Engine.fromPosts = function(options) {
return ajax(options.path).then(Engine.parsePosts).then(function(posts) {
return new Engine(posts, options);
});
};
…
Engine.fromPosts({path: '/path/to/posts'}).then(function(engine) {
engine.registerWith(framework).then(function(framePage) {
engine.showPostsOn(framePage);
});
});
I encountered the same problem and came up with this simple solution.
Instead of returning a Promise from the constructor, put it in this._initialized property, like this:
function Engine(path) {
this._initialized = Promise.resolve()
.then(() => {
return doSomethingAsync(path)
})
.then((result) => {
this.resultOfAsyncOp = result
})
}
Then, wrap every method in a callback that runs after the initialization, like that:
Engine.prototype.showPostsOnPage = function () {
return this._initialized.then(() => {
// actual body of the method
})
}
How it looks from the API consumer perspective:
engine = new Engine({path: '/path/to/posts'})
engine.showPostsOnPage()
This works because you can register multiple callbacks to a promise and they run either after it resolves or, if it's already resolved, at the time of attaching the callback.
This is how mongoskin works, except it doesn't actually use promises.
Edit: Since I wrote that reply I've fallen in love with ES6/7 syntax so there's another example using that.
class Engine {
constructor(path) {
this._initialized = this._initialize(path)
}
async _initialize() {
// actual async constructor logic
this.resultOfAsyncOp = await doSomethingAsync(path)
}
async showPostsOnPage() {
await this._initialized
// actual body of the method
}
}
To avoid the separation of concerns, use a factory to create the object.
class Engine {
constructor(data) {
this.data = data;
}
static makeEngine(pathToData) {
return new Promise((resolve, reject) => {
getData(pathToData).then(data => {
resolve(new Engine(data))
}).catch(reject);
});
}
}
The return value from the constructor replaces the object that the new operator just produced, so returning a promise is not a good idea. Previously, an explicit return value from the constructor was used for the singleton pattern.
The better way in ECMAScript 2017 is to use a static methods: you have one process, which is the numerality of static.
Which method to be run on the new object after the constructor may be known only to the class itself. To encapsulate this inside the class, you can use process.nextTick or Promise.resolve, postponing further execution allowing for listeners to be added and other things in Process.launch, the invoker of the constructor.
Since almost all code executes inside of a Promise, errors will end up in Process.fatal
This basic idea can be modified to fit specific encapsulation needs.
class MyClass {
constructor(o) {
if (o == null) o = false
if (o.run) Promise.resolve()
.then(() => this.method())
.then(o.exit).catch(o.reject)
}
async method() {}
}
class Process {
static launch(construct) {
return new Promise(r => r(
new construct({run: true, exit: Process.exit, reject: Process.fatal})
)).catch(Process.fatal)
}
static exit() {
process.exit()
}
static fatal(e) {
console.error(e.message)
process.exit(1)
}
}
Process.launch(MyClass)
This is in typescript, but should be easily converted to ECMAscript
export class Cache {
private aPromise: Promise<X>;
private bPromise: Promise<Y>;
constructor() {
this.aPromise = new Promise(...);
this.bPromise = new Promise(...);
}
public async saveFile: Promise<DirectoryEntry> {
const aObject = await this.aPromise;
// ...
}
}
The general pattern is to store the promises as internal variables using the constructor and await for the promises in the methods and make the methods all return promises. This allows you to use async/await to avoid long promise chains.
The example I gave is good enough for short promises, but putting in something that requires a long promise chain will make this messy, so to avoid that create a private async method that will be called by the constructor.
export class Cache {
private aPromise: Promise<X>;
private bPromise: Promise<Y>;
constructor() {
this.aPromise = initAsync();
this.bPromise = new Promise(...);
}
public async saveFile: Promise<DirectoryEntry> {
const aObject = await this.aPromise;
// ...
}
private async initAsync() : Promise<X> {
// ...
}
}
Here is a more fleshed out example for Ionic/Angular
import { Injectable } from "#angular/core";
import { DirectoryEntry, File } from "#ionic-native/file/ngx";
#Injectable({
providedIn: "root"
})
export class Cache {
private imageCacheDirectoryPromise: Promise<DirectoryEntry>;
private pdfCacheDirectoryPromise: Promise<DirectoryEntry>;
constructor(
private file: File
) {
this.imageCacheDirectoryPromise = this.initDirectoryEntry("image-cache");
this.pdfCacheDirectoryPromise = this.initDirectoryEntry("pdf-cache");
}
private async initDirectoryEntry(cacheDirectoryName: string): Promise<DirectoryEntry> {
const cacheDirectoryEntry = await this.resolveLocalFileSystemDirectory(this.file.cacheDirectory);
return this.file.getDirectory(cacheDirectoryEntry as DirectoryEntry, cacheDirectoryName, { create: true })
}
private async resolveLocalFileSystemDirectory(path: string): Promise<DirectoryEntry> {
const entry = await this.file.resolveLocalFilesystemUrl(path);
if (!entry.isDirectory) {
throw new Error(`${path} is not a directory`)
} else {
return entry as DirectoryEntry;
}
}
public async imageCacheDirectory() {
return this.imageCacheDirectoryPromise;
}
public async pdfCacheDirectory() {
return this.pdfCacheDirectoryPromise;
}
}
I'm using the JayData.js library. It works quite well. However, I have a few situations where I've got a toArray() call deep in the function tree. Rather than trying to access my "busy" signal from there, I'd just as soon have the method block. Is that possible? I'm picturing something like "context.Groups.toArray(myObservableVar).block()".
Update 1: It appears that the JayData library returns a jQuery deferred object judging from the use of "then" and "when" operators on the return value. Is there a corresponding method to "join" -- meaning wait for the finish?
Indeed JayData toArray() (and all relevant data returning or saving/updating method) implements jQuery deferred. As from 1.0.5 you have to include the JayDataModules/deferred.js in order to this functionality to work.
For your use case $.when might be an answer:
var customers = context.Customers.toArray();
var products = context.Products.toArray();
var suppliers = context.Suppliers.toArray();
$.when(customers, products, suppliers).then(function(customers, products, suppliers) {
//we have everything here
//notice the the parameter names are shadowed as the var customers variable only
//holds a promise not the value
customers.forEach( ... );
products[12].ProductName = "X";
});
A blockUntilDone() method would go against the principles of deferred execution and continuations. JayData's toArray() is asynchronous because it is designed not to block the caller.
If you want this kind of code:
// Initialize context and groups...
var arrayOfGroups = context.Groups.toArray(); // Want synchronous behavior.
// Do something with 'arrayOfGroups'...
Trying to block until the deferred is resolved is not the solution. Move the last part of your code into a callback passed to toArray() instead:
// Initialize context and groups...
context.Groups.toArray(function(arrayOfGroups) {
// Do something with 'arrayOfGroups'...
});
Alternatively, bind to the returned promise with done() or then():
context.Groups.toArray().done(function(arrayOfGroups) {
// Do something with 'arrayOfGroups'...
});