There is an abstarct class Layer:
export abstract class Layer {
protected layers: any[] = [];
abstract get(): Promise<Layer[]>;
}
Concrete class:
export class CommonLayers extends Layer {
async get(): Promise<Layer[]> {
if (!hasRole('admin')) return;
/* items belows could be loaded from server, now it is empty array
this.layers.push({
title: "Title",
items: [],
count: 3,
});
return this.layers;
}
}
Using:
async getLayers(): Promise<any[]> {
// let thematicLayers = await new ThematicLayers();
let customLayers = await new CommonLayers();
return [...thematicLayers, ...customLayers];
}
I dislike this part of code:
this.layers.push({
title: "Title",
items: [],
count: 3,
});
return this.layers;
This method does a few things.
Checks if user has admin role
Gets data from response if there is
Prepares data
Sets data to this.layers
Returns this.layers
I think this method has a lot responsibilities. So when programmer calls the method get() he assumes it just returns data.
And it look not readable!
How to fix it?
You need a method that fetches the data from the server. That requires a check if the user is an admin (note this should really be happening on the back end, but whatever), and there is no reason not to return the fetched data. I don't see a problem here, as far as it goes.
The problem is that you are correct that using Array.prototype.push in an async function or .then callback is a code smell and you don't provide any way to get at the data other than to access the property synchronously (which will expose you to all sorts of timing bugs) or call the method that fetches from the server (unnecessary http calls).
There really isn't any way to square this circle, I would just say that you should stay async all the way:
class Foo {
// this should be a specific type rather than any
protected layers: Promise<any[]> = Promise.resolve([])
// I strongly recommend picking a method name other than 'get'
async get(): Promise<any[]> {
if (!hasRole('admin')) return [];
const response = await fetch(someURL);
this.layers = response.json();
return this.layers;
}
}
Playground
This is less convenient for callers, but there isn't necessarily a robust way around it.
What I would split out into a second (or more) method is if you do any logic on the frontend with the returned data. But if all you're doing is fetching it (conditional on being an admin) then I think it's fine.
Related
I need to instantiate an object asynchronously. But the constructor method cannot be declared async async constructor(){} and be called like so async new MyClass(). That would be strange anyway. Therefore, some kind of factory is needed. I also want to hide the constructor. This would be very easy to achieve if the constructor could be declared private: #constructor. But even with ES2022 private class features this is not possible. However, there are several patterns to solve these two problems. I will list 3 of them (in the context of ES6 modules).
I would like to know which pattern, if any, are considered good and which are not. Or if there are even better solutions, please let me know.
Method I: static boolean (without a factory)
let calledInit = false;
export default class MyClass {
constructor(data) {
if (calledInit === false)
throw Error('private constructor');
calledInit = false;
// member initializations here
}
static async initialize() {
calledInit = true;
// do some async stuff here
return new MyClass(data);
}
}
Method II: closure and singleton-factory
const MyClass = (function() {
class MyClass {
constructor(data) {
// member initializations here
}
}
return {
initialize: async function() {
// do some async stuff here
return new MyClass(data);
}
}
})();
Object.freeze(MyClass);
export default MyClass;
use them both like so:
const obj = await MyClass.initialize();
Method III: factory function
class MyClass {
constructor(data) {
// member initializations here
}
}
export default async function MyClassInitializer() {
// do some async stuff here
return new MyClass(data);
}
use it like so:
const obj = await MyClassInitializer();
Edit: To clarify, I believe hiding the constructor is beneficial. Mainly for debugging purpose. Let's say the constructor is public and someone else is instantiating the object using the new operator. It always fails with obscure error messages and she/he can't make sense of it. All this, because she/he didn't carefully read the docs which clearly states to use the initialize() method to create an instance. This could easily have been prevented by hiding the constructor in the first place.
Disclaimer This is my opinion
Instantiating an object asynchronously is not really a thing for a good reason.
The purpose of the constructor is to construct your object.
You should not put something like load operations from a file, API etc. in the constructor.
Put them into a separate function and use the result as an argument to your class.
async () => {
const data = await fetch(...) // get data / do some sideeffect
const myObject = new MyObject(data)
}
This is also usually desirable since you can update some state to track the progress of the request. Or you might want to cache it and use it in multiple places.
async () => {
state = "LOADING"
const data = await fetch(...) // get data / do some sideeffect
state = "SUCCESS"
const myObject = new MyObject(data)
}
Hiding the constructor is not really needed since you get an error when you do not pass data to it. And the user of your class should know what to pass. If you use typescript, this would be even more strict.
Sure you can put this in one initialize function as in method 3.
So I have an Angular component.
With some array objects containing data I want to work with:
books: Book[] = [];
reviews: Review[] = [];
This is what my ngOnInit() looks like:
ngOnInit(): void {
this.retrieveBooks();
this.retrieveReviews();
this.setRatingToTen();
}
With this I write Books, and Reviews to object arrays.
This is done through a "subscription" to data through services:
retrieveBooks(): void {
this.bookService.getAll()
.subscribe(
data => {
this.books = data;
},
error => {
console.log(error);
}
);
}
retrieveReviews(): void {
this.reviewService.getAll()
.subscribe(
data => {
this.reviews = data;
},
error => {
console.log(error);
});
}
So this next function I have is just an example of "working with the data".
In this example, I just want to change all of the totalratings to 10 for each Book:
setRatingToTen(): void {
this.books.forEach(element => {
element.totalrating = 10;
});
console.log(this.books);
}
The problem I have been trying to wrap my head around is this:
this.books is an empty array.
I THINK the reason is because this function is running before the data subscription.
IF this is the case, then my understanding of ngOnInit must not be right.
I thought it would call the function in order.
Maybe that's still the case, it's just that they don't complete in order.
So my questions are:
1. Why is it an empty array?
(was I right? or is there more to it?)
2. How do Angular developers write functions so they operate in a desired order?
(since the data needs to be there so I can work with it, how do I avoid this issue?)
(3.) BONUS question:
(if you have the time, please and thank you)
My goal is to pull this.reviews.rating for each book where this.reviews.title equals this.books.title, get an average score; and then overwrite the "0" placeholder of this.books.totalrating with the average. How could I re-write the setRatingToTen() function to accomplish this?
Here is one of solution using forkJoin method in rxjs .
you can check this for details https://medium.com/#swarnakishore/performing-multiple-http-requests-in-angular-4-5-with-forkjoin-74f3ac166d61
Working demo : enter link description here
ngOnInit:
ngOnInit(){
this.requestDataFromMultipleSources().subscribe(resList => {
this.books = resList[0];
this.reviews = resList[1];
this.setRatingToTen(this.books,this.reviews);
})
}
forkJoin method:
public requestDataFromMultipleSources(): Observable<any[]> {
let response1 = this.retrieveBooks();
let response2 = this.retrieveReviews();
// Observable.forkJoin (RxJS 5) changes to just forkJoin() in RxJS 6
return forkJoin([response1, response2]);
}
Other methods:
retrieveBooks(): void {
this.bookService.getAll()
.subscribe(
data => {
this.books = data;
},
error => {
console.log(error);
}
);
}
retrieveReviews(): void {
this.reviewService.getAll()
.subscribe(
data => {
this.reviews = data;
},
error => {
console.log(error);
});
}
setRatingToTen(books, reviews): void {
this.books.forEach(element => {
element.totalrating = 10;
});
console.log(this.books);
}
Angular makes heavy use of observables to handle variety of asynchronous operations. Making server side requests (through HTTP) is one of those.
Your first two questions clearly reflect you are ignoring the asynchronous nature of observables.
Observables are lazy Push collections of multiple values. detailed link
Means observable response would be pushed over time in an asynchronous way. You can not guarantee which of the two distinct functions would return its response first.
Having said that, rxjs library (Observables are also part of this library and angular borrowed them from here) provides a rich collection of operators that you can use to manipulate observables.
With the above explanation, here is one by one answer to your questions.
1. Why is it an empty array?
Because you are thinking in terms of synchronous sequential flow of code, where one method would get called only after the other has finished with its working. But here retrieveBooks and retrieveReviews both are making asynchronous (observable) calls and then subscribing to it. This means there is no guarantee when their response would be received. Meanwhile the hit to setRatingToTen had already been made, at that point in time books array was empty.
2. How do Angular developers write functions so they operate in a desired order?
Angular developer would understand the nature of asynchronous observable calls, and would pipe the operators in an order so that they are sure they have the response in hand before performing any further operation on the observable stream.
(3.) BONUS question:
Your requirement specifies that you must first have the response of both observables at hand before performing any action. For that forkJoin rxjs operator suits your need. Documentation for this operator say
One common use case for this is if you wish to issue multiple requests on page load (or some other event) and only want to take action when a response has been received for all. detailed link
Not sure about your average score strategy, but here is an example code how you would achieve your purpose.
ngOnInit(){
let req1$ = this.bookService.getAll();
let req2$ = this.reviewService.getAll();
forkJoin(req1$, req2$).subscribe(([response1, response2])=>{
for(let book of response1) //loop through book array first
{
for(let review of response2) //inner loop of reviews
{
if(book.title == review.title)
{
//complete your logic here..
}
}
}
});
}
Performance question - I'm trying to understand if i have a node http server powered by express , is it consider bad practice to create new class instance on every request sent by the user?
The class instance fetch data from another api and expose some functionality to manipulate the fetched data.
example of the code:
//--- Handler.js ---
const _ = require("lodash");
class Handler {
constructor() {
this.fetchData = this.getSiteModel.bind(this);
this.getA = this.getA.bind(this);
this.getB = this.getB.bind(this);
this.getC = this.getC.bind(this);
}
async fetchData(req,res,id){
const result = await fetch(...)
this.data = result;
}
getA(){
...
return this.data.A
}
getB(){
...
return this.data.B
}
getC(){
...
return this.data.C
}
}
//---- controller.js ----
const Handler = require("../Handler/");
exports.getDataById = async function(req ,res) {
const handler = new Handler();
return handler.getA();
}
Would it be better to do this instead
//---- controller.js ----
const fetchData = require("../Handler/");
const getA = require("../Handler/getA");
const getB = require("../Handler/getB");
const getC = require("../Handler/getC");
exports.getDataById = async function(req ,res) {
//no new handler instance created
const data = fetchData(url)
return getA(data);
}
Is it considered bad practice to create new class instance on every request sent by the user?
No, it's not generically considered a bad practice. It's normal to need to create objects during the processing of an incoming request. Look at any database query which is often used during a request handler. It will likely create multiple objects.
Now, whether or not there is a more efficient way to do what you're doing is a different question and we would need to see your actual code in order to offer some advice on that topic.
Would it be better to do this instead?
I don't see a whole lot of reasons for you to put the data into an object before making a single operation on that object.
Something like you proposed (with the addition of await to make it work properly):
exports.getDataById = async function(req ,res) {
//no new handler instance created
const data = await fetchData(url)
return getA(data);
}
Seems perfectly fine to me. When to structure things into a class and put the data into the instance data and why to just use a function to operate on the data is a classic OOP question and it depends upon lots of things which are all dependent upon seeing and understanding your real code, what you're doing with it, how it is most likely to be expanded in the future, how often you are calling multiple functions on the same data in one request, etc...
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 want to create a class whose duty is to poll data sources, collate information into an array of 'alert' objects, and then deliver a subset of those alerts to any other class that wants them.
Because polling happens asynchronously (I'm requesting data from a web service) then I assume that what I actually need to return is a promise which, when fulfilled, will give the correct subset of Alert objects.
But clearly I don't understand how to do this, because the method that is supposed to return the promise returns something else.
Here's my code so far. As you can see, I'm trying to store the promise in an instance attribute and then retrieve it:
export class AlertCollection {
constructor() {
this.alerts = null;
}
// poll the data sources for alert data; store a promise that resolves
// to an array of alerts
poll() {
this.alerts = this.pollTeapot()
.then( (arr) => {this.pollDeliverance(arr);} );
}
// return a promise that fulfils to an array of the alerts you want
filteredAlerts(filter) {
return this.alerts; // not filtering for now
}
// return a promise that fulfills to the initial array of alerts
pollTeapot() {
let process = (json) => {
json2 = JSON.parse(json);
return json2.map( (a) => new Alert(a) );
};
message = new MessageHandler("teapot", "alerts")
return message.request().then( (json) => {process(json);} );
}
// Modify the alerts based on the response from Deliverance.
// (But for the time being let's not, and say we did.)
pollDeliverance(alerts) {
return alerts;
}
}
message.request() returns a promise from the web service. That works. If I snapshot the process function inside pollTeapot() I get the right data.
But, if I snapshot the return value from filteredAlerts() I don't get that. I don't get null either (which would at at least make sense, although it would be wrong.) I get something like { _45: 0, _81: 0, _65: null, _54: null }.
Any pointers would be very much appreciated at this point. (This is in React Native, by the way, if that helps.)
I am not sure if I understood your problem fully, but I will try to give you an generic solution to chaining promises one after another.
someAsyncFunction().then(dataFromAsync1 => {
return anotherAsyncFunction(dataFromAsync1).then(dataFromAsync2 => {
return doSomethingWithData(dataFromAsync1, dataFromAsync2);
});
});
This is going to be a hard one to describe - I have a working example but it's convoluted in that I've had to "mock up" all of the async parts, and use function classes rather than the class keyword - but the idea is the same!
There are 2 parts to this answer.
It does not make sense to store alerts as an instance variable. They are asynchronous, and wont exist until after the async calls have completed
You'll need to chain all of your behaviour onto the initial call to poll
In general, you chain promises on to one another like this
functionWhichReturnsPromise()
.then(functionPointer)
.then(function(result){
// some functionality, which can return anything - including another promise
});
So your code would end up looking like
var alertCollection = new AlertCollection()
alertCollection.poll().then(function(alerts){
//here alerts have been loaded, and deliverance checked also!
});
The code of that class would look along the lines of:
export class AlertCollection {
constructor() {
}
// poll the data sources for alert data; store a promise that resolves
// to an array of alerts
poll() {
return this.pollTeapot()
.then(filteredAlerts)
.then(pollDeliverance);
}
// return a promise that fulfils to an array of the alerts you want
filteredAlerts(alerts) {
return alerts; // not filtering for now
}
// return a promise that fulfills to the initial array of alerts
pollTeapot() {
let process = (json) => {
json2 = JSON.parse(json);
return json2.map( (a) => new Alert(a) );
};
message = new MessageHandler("teapot", "alerts")
return message.request().then(process);
}
// Modify the alerts based on the response from Deliverance.
// (But for the time being let's not, and say we did.)
pollDeliverance(alerts) {
return alerts;
}
}
A few notes
filteredAlerts can do whatever you like, so long as it returns an array of results
pollDeliverance can also do whatever you like - if it needs to call another async method, remember to return a promise which resolves to an array of alerts - perhaps updated from the result of the async call.
I have created a JSFiddle which demonstrates this - using a simple getJSON call to replicate the async nature of some of this. As I mentioned, it is convoluted, but demonstrates the process:
Live example: https://jsfiddle.net/q1r6pmda/1/