I was asked to create a singleton that will have methods which it will operate on some data it holds. The data is loaded from a nearby CSV file, but I cannot find a way to do it in an asynchronous way.
This is a Vanilla JavaScript assignment.
I have this CSV file, which I want to load/ read only once, and then "hold" inside of my singleton. Since all of the singleton's methods regard this data, I don't think there is a reason to read it more than once, at the beginning.
Since reading (and parsing) it is a asynchronous work, and so are some expected methods, I can't find the right way/ place/ method to do it.
Searching the internet shows that if I choose to create a class, the constructor must NOT by asynchronous.
An IIFE pattern did not work for me as well...
I currently have no code, since nothing seems to work, unfortunately.
Any help will be highly appreciated!
There are a few ways you can do this. All of the approaches are fine, so it is personal preference.
1. Static Method
class CsvHandler {
static readFile(fileName) {
return new Promise(resolve => {
const instance = new CsvHandler()
// Process your csv file here
resolve(instance);
}
}
}
const csvHandler = CsvHandler.readFile('fileName.txt')
2. Factory Method
class CsvHandler {}
class CsvFactory {
readFile(fileName) {
return new Promise(resolve => {
const instance = new CsvHandler()
// Process your csv file here
resolve(instance);
}
}
}
var csvFactory = new CsvFactory(),
csvHandler = csvFactory.readFile('fileName.txt')
You can add a method to your class that builds an instance of the class with the required data having fetched the CSV file.an example is this:
class Importer{
private importerInstance: Importer
private constructor(private csvData){
...
}
// returns an instance of the importer
static async composeInstance(path to CSV){
... retrieve file content asynchronously
if(!this.importerInstance) {return new Importer(<result of async csv fetching>)} else {return this.importerInstance}
}
}
Let me know if it makes any sense. I could try to do a more elaborate example for you
Related
I am gradually improving a codebase that originally had some AngularJs in various versions and some code that was not in a framework at all using various versions of a software API. (For some reason this API is available - to pages loaded through the application - on AngularJS's $window.external...go figure.)
In my pre-ES6, AngularJs 1.8 phase, I have three services that interact with the software's API (call them someAPIget, someAPIset, and someAPIforms). Something like this:
// someAPIget.service.js
;(function () {
var APIget = function ($window, helperfunctions) {
function someFunc (param) {
// Do something with $window.external.someExternalFunc
return doSomethingWith(param)
}
return {
someFunc: someFunc
}
}
angular.module('someAPIModule').factory('someAPIget', ['$window', 'helperfunctions', someAPIget])
})()
I then had a service and module a level up from this, with someAPIModule as a dependency, that aggregated these functions and passed them through under one name, like this:
// apiinterface.service.js
;(function () {
// Change these lines to switch which API service all functions will use.
var APIget = 'someAPIget'
var APIset = 'someAPIset'
var APIforms = 'someAPIforms'
var APIInterface = function (APIget, APIset, APIforms) {
return {
someFunc: APIget.someFunc,
someSettingFunc: APIset.someSettingFunc,
someFormLoadingFunc: APIforms.someFormLoadingFunc
}
}
angular.module('APIInterface').factory('APIInterface', [APIget, APIset, APIforms, APIInterface])
})()
I would then call these functions in various other controllers and services by using APIInterface.someFunc(etc). It worked fine, and if we switch to a different software provider, we can use our same pages without rewriting everything, just the interface logic.
However, I'm trying to upgrade to Typescript and ES6 so I can use import and export and build some logic accessible via command line, plus prepare for upgrading to Angular 11 or whatever the latest version is when I'm ready to do it. So I rebuilt someAPIget to a class:
// someAPIget.service.ts
export class someAPIget {
private readonly $window
private readonly helperfunctions
static $inject = ['$window', 'helperfunctions']
constructor ($window, helperfunctions) {
this.$window = $window
this.helperfunctions = helperfunctions
}
someFunc (param) {
// Do something with this.$window.external.someExternalFunc
return doSomethingWith(param)
}
}
}
angular
.module('someAPImodule')
.service('someAPIget', ['$window', 'helperfunctions', someAPIget])
Initially it seemed like it worked (my tests still pass, or at least after a bit of cleanup in the Typescript compilation department they do), but then when I load it into the live app... this.$window is not defined. If, however, I use a direct dependency and call someAPIget.someFunc(param) instead of through APIInterface.someFunc(param) it works fine (but I really don't want to rewrite thousands of lines of code using APIInterface for the calls, plus it will moot the whole point of wrapping it in an interface to begin with). I've tried making APIInterface into a class and assigning getters for every function that return the imported function, but $window still isn't defined. Using console.log statements I can see that this.$window is defined inside someFunc itself, and it's defined inside the getter in APIInterface, but from what I can tell when I try to call it using APIInterface it's calling it without first running the constructor on someAPIget, even if I make sure to use $onInit() for the relevant calls.
I feel like I am missing something simple here. Is there some way to properly aggregate and rename these functions to use throughout my program? How do alias them correctly to a post-constructed version?
Edit to add: I have tried with someAPIget as both a factory and a service, and APIInterface as both a factory and a service, and by calling APIInterface in the .run() of the overall app.module.ts file, none of which works. (The last one just changes the location of the undefined error.)
Edit again: I have also tried using static for such a case, which is somewhat obviously wrong, but then at least I get the helpful error highlight in VSCode of Property 'someProp' is used before its initialization.ts(2729).
How exactly are you supposed to use a property that is assigned in the constructor? How can I force AngularJS to execute the constructor before attempting to access the class's members?
I am not at all convinced that I found an optimal or "correct" solution, but I did find one that works, which I'll share here in case it helps anyone else.
I ended up calling each imported function in a class method of the same name on the APIInterface class, something like this:
// apiinterface.service.ts
// Change these lines to switch which API service all functions will use.
const APIget = 'someAPIget'
const APIset = 'someAPIset'
const APIforms = 'someAPIforms'
export class APIInterface {
private readonly APIget
private readonly APIset
private readonly APIforms
constructor (APIget, APIset, APIforms) {
this.APIget = APIget
this.APIset = APIset
this.APIforms = APIforms
}
someFunc(param: string): string {
return this.APIget.someFunc(param)
}
someSettingFunc(param: string): string {
return this.APIset.someSettingFunc(param)
}
someFormLoadingFunc(param: string): string {
return this.APIforms.someFormLoadingFunc(param)
}
}
angular
.module('APIInterface')
.factory('APIInterface', [APIget, APIset, APIforms, APIInterface])
It feels hacky to me, but it does work.
Later Update:
I am now using Angular12, not AngularJS, so some details may be a bit different. Lately I have been looking at using the public-api.ts file that Angular12 generates to accomplish the same thing (ie, export { someAPIget as APIget } from './filename' but have not yet experimented with this, since it would still require either consolidating my functions somehow or rewriting the code that consumes them to use one of three possible solutions. It would be nice not to have to duplicate function signatures and doc strings however. It's still a question I'm trying to answer more effectively, I will update again if I find something that really works.
I'm trying to write a test using JEST to a class I wrote with static properties that resembles the following:
class DataManager {
static #data = null;
static getData = () => {
return this.#data;
}
static initializeData = async () => {
await database(async (db) => {
const data = getSomeDataFromDatabase() //just example
this.#data = data;
});
}
}
Now, I want to make new implementation for my initializeData method, to returned some mocked data instead of going to the db, and then "getData" to see if I get the expected result. The problem is that my #data property is private and I don't want to expose it to the outside world. Is there any way to do it?
No, there is no way to do that. Private fields are really private. If you want to mock them in a test, don't use private fields. Or the other way round: if they're private (an implementation detail), you shouldn't mock them.
You need to either mock the entire DataManager class and its public methods, or if you want to test the DataManager itself then you'd mock the database and getSomeDataFromDatabase functions that it calls.
I'd like to use the window.showDirectoryPicker() method from Dart/Flutter. Using it from JavaScript to allow a user to select a directory and then list the contents of it works like this:
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
console.log(entry.name);
}
The docs for FileSystemDirectoryHandle.values() say it returns "a new Array Iterator containing the values for each index in the FileSystemDirectoryHandle object." It doesn't say specifically, though it appears to be an async iterator as enumerating it requires await for.
I'm trying to create bindings to call this from Dart. I expect that my binding should return a Stream<FileSystemHandle> although I can't find any examples or docs about doing this. I've tried many combinations of things, though here's what I have now:
#JS()
library js_lib;
import 'dart:html';
import 'package:js/js.dart';
import 'package:js/js_util.dart';
Future<FileSystemDirectoryHandle> showDirectoryPicker() =>
promiseToFuture(callMethod(window, 'showDirectoryPicker', []));
#JS()
abstract class FileSystemDirectoryHandle extends FileSystemHandle {
external Stream<FileSystemHandle> values();
}
#JS()
abstract class FileSystemHandle {
String get name;
}
#JS()
abstract class FileSystemFileHandle extends FileSystemHandle {}
I'm trying to call it like this:
final dirHandle = await showDirectoryPicker();
await for (final item in dirHandle.values()) {
print(item.name);
}
However this fails like with the error:
Error: Expected a value of type 'Stream', but got one of type 'NativeJavaScriptObject'
I suspect I may need something to convert the JS iterator to a Dart stream (similar to the promiseToFuture call I have), although I can't find a way to do that (I'm also unable to add methods to FileSystemDirectoryHandle that aren't external.
Edit: This seems to work in build_runner serve but not build_runner serve --release. I've filed an issue to determine if this is a bug or this is the wrong way to do this.
After realising the async iterators in JS are just iterators where next() returns a promise, I was able to write a small function that just invoked the method manually and yielded in an async* function:
extension FileSystemDirectoryHandleExtensions on FileSystemDirectoryHandle {
Stream<FileSystemHandle> values() =>
_asyncIterator<FileSystemHandle>(callMethod(this, 'values', []));
}
Stream<T> _asyncIterator<T>(jsIterator) async* {
while (true) {
final next = await promiseToFuture(callMethod(jsIterator, 'next', []));
if (getProperty(next, 'done')) {
break;
}
yield getProperty(next, 'value');
}
}
This is called using the original Dart code above, and prints the filenames as desired.
I'm taking an online web development course. I've come across some code that is confusing me. Within the index.js file there is a variable titled defaultCardList which instantiates a new instance of Section class. However, before the declaration is finished, within the renderer property, the defaultCardList variable is used to call the setItem method.
How is it possible to call the method from the defaultCardList variable before it's completely declared?
const defaultCardList = new Section({
data: items,
renderer: (item) => {
const card = new DefaultCard(item, ".default-card");
const cardElement = card.generateCard();
defaultCardList.setItem(cardElement); //How can this be called before its completion?
}
}, cardListSelector);
I want a JavaScript class that can conditionally add additional methods into itself from separate files. The idea is to separate different concerns of the app into more manageable self-contained modules that nevertheless can interact with the methods in the mother app class. Therefore, the additional methods in the separate file must be able to reference the methods and variables in the main class. See the code sample below.
I looked at a lot of different solutions, but all of them have a downside for what I'm wanting.
I could do new Uploads(), but I could not find a way for methods in Uploads.js to reference methods in the main App class
I could use extends, but this would not allow for conditional extension AFAIK
I could just define new methods into the prototype itself, but this would mean that the external file would need to "know" about the class it's going to be used in, which doesn't make it widely reusable.
The best I have so far is the following:
app.js
const Uploads = require("./Uploads.js");
const config = { hasUploads: true }; // Probably loaded from a file
class App {
constructor() {
/* Only include the separate module if the config says so */
if(config.hasUploads) {
Object.assign(this, Uploads);
}
}
foo() {
/* Something */
}
}
Uploads.js
module.exports = {
bar() {
this.foo();
}
};
It works, but I don't know if this is the best solution;
There's no constructor, so if Uploads.js needs to do some setup, app.js needs to contain the logic to do so (or at least know to call some uniquely named faux constructor method), which doesn't seem ideal.
If Uploads.js contains a method with the same name as app.js or any other possible module being loaded, they will be overwritten, leading into unexpected behaviour.
The Uploads.js is an object of functions, whereas app.js defines a class. Ideally (though I guess not necessarily) for code manageability they should both use the same syntax.
Is there a better/cleaner/nicer way of doing this?
Instead of trying to perform some kind of crazy multi inheritance, why not try embracing composition? Its very good for solving these kinds of problems.
class App {
constructor(modules) {
if (modules.uploads) {
this.uploads = modules.uploads(this);
}
}
foo() {
console.log('foo!');
}
}
class Uploads {
constructor(context) {
this.context = context;
}
method() {
this.context.foo();
}
}
const app = new App({ uploads: (ctx) => new Uploads(ctx) });
app.uploads.method();
You can get really fancy with this and use builders to configure apps with specific types of modules.
Depending on your anticipated complexity, you might want to think about using event buses, mediators, or commands to decouple things from the host itself.
One option to fix overwriting an existing method from the uploads file is to assign new methods in a loop and check for duplicates (Object.assign is not ideal in this case) and only add updates once:
const Uploads = {
bar() {
this.foo("called from bar");
}
};
const config = { hasUploads: true, // Probably loaded from a file
configured: false
};
class App {
constructor() {
/* Only include the separate module if the config says so */
if(config.hasUploads && !config.configured) {
const proto = this.constructor.prototype;
const methods = Object.keys(Uploads);
methods.forEach( name=> {
if( proto[ name] ) {
throw new Error( "App already has method " + name);
}
proto[name] = Uploads[name];
});
config.configured = true;
}
}
foo(arg) {
/* Something */
console.log( arg );
}
}
const app = new App();
app.bar();
A better (cleaner) alternative might be to add updates to the class before calling its constructor, using a static class method because its this value is the constructor function. Tested example:
static addMethods(uploads) { // inside class declaration
const proto = this.prototype;
for (const [name, method] of Object.entries(uploads)) {
if( proto[name]) {
throw new Error("App already has a ${name} method");
}
proto[name] = method;
}
}
to be called as needed by
if( config.hasUploads) {
App.addMethods( Uploads);
}