This question already has an answer here:
Asynchronous operations in constructor
(1 answer)
Closed 3 years ago.
I am trying to implement the Customer object in NodeJs and that the instance is able to collect its data.
class CustomerModel extends Model {
public customer
constructor(email:string) {
super();
this.collection = 'customer';
this.customer = await CustomerLayer.getCustomerByEmail(email);
}
}
But I can't have an asynchronous constructor. I have seen that in Javascript you can do the following:
const sleep = () => new Promise(resolve => setTimeout(resolve, 5000));
class Example {
constructor () {
return new Promise(async (resolve, reject) => {
try {
await sleep(1000);
this.value = 23;
} catch (ex) {
return reject(ex);
}
resolve(this);
});
}
}
(async () => {
// It works as expected, as long as you use the await keyword.
const example = await new Example();
console.log(example instanceof Example);
console.log(example.value);
})();
But I think it is not correct to return the data from the constructor.
Is there any correct way to call asynchronous methods from the constructor?
I wouldn't do it in constructor. Probably init method fits this use case better.
class CustomerModel extends Model {
public customer
constructor(email:string) {
super();
this.collection = 'customer';
}
async init() {
this.customer = await CustomerLayer.getCustomerByEmail(email);
}
}
const myClass = new CustomerModel();
await myClass.init();
You could also consider creating a static method to return the instance which internally create object and init.
It's not possible. You have a few options here:
You can explicitly return a Promise from the constructor (construct it via new Promise). This is what you're doing in your second code. await can always be substituted with the Promise constructor and .then. But, it's pretty weird, since one would always expect the result of new Example() to then be an instanceof Example - one wouldn't expect new Example() to result in a Promise. This is what your first code would look like using this method:
class CustomerModel extends Model {
public customer
constructor(email:string) {
super();
this.collection = 'customer';
return CustomerLayer.getCustomerByEmail(email)
.then((customerByEmailResolveValue) => {
this.customerByEmailResolveValue = customerByEmailResolveValue;
return this;
});
}
}
const customerProm = new CustomerModel('foo');
customerProm.then((actualCustomerInstance) => {
// use actualCustomerInstance
});
In the constructor, assign the Promise to a property of the instance. Then, when you need to consume that value, call .then on the property:
class CustomerModel extends Model {
public customer
constructor(email:string) {
super();
this.collection = 'customer';
this.customerProm = CustomerLayer.getCustomerByEmail(email);
}
}
const customer = new CustomerModel('foo');
customer.customerProm.then((result) => {
// use result;
});
You can also retrieve the value before creating the instance, then pass it in:
class CustomerModel extends Model {
public customer
constructor(email:string, customerByEmailResolveValue) {
super();
this.collection = 'customer';
this.customer = customerByEmailResolveValue;
}
}
CustomerLayer.getCustomerByEmail(email)
.then((customerByEmailResolveValue) => {
const customer = new CustomerModel('foo', customerByEmailResolveValue);
});
Related
I have the following code:
class simplePromise {
constructor(resolveFn, rejectFn) {
console.log(resolveFn, 'resolveFn') // (resolutionFunc, rejectionFunc) => {
// resolutionFunc(777);
// rejectionFunc();
// }
console.log(rejectFn, 'rejectFn') //undefined
}
}
const promise1 = new simplePromise( (resolutionFunc, rejectionFunc) => {
resolutionFunc(777);
rejectionFunc();
});
As you can see, I'm trying to pass in 2 functions to the constructor. However, when I console.logged each of them out, I noticed that both functions are registered as 1 argument. In this case, how do I separate the 2 functions?
Are you sure that you want to pass 2 functions to the constructor?
const promise1 = new simplePromise( (resolutionFunc, rejectionFunc) => {...}
this part of your code looks more like you want a single callback function that recieves 2 functions.
Like this:
class SimplePromise {
constructor(callback) {
const resolveFn = (value) => {
console.log("resolved with value", value);
};
const rejectFn = (error) => {
console.log("rejected with error", error);
}
callback(resolveFn, rejectFn);
}
}
const promise1 = new SimplePromise((resolutionFunc, rejectionFunc) => {
console.log('resolutionFunc', resolutionFunc)
console.log('rejectionFunc', rejectionFunc) //undefined
resolutionFunc(777);
rejectionFunc();
});
Not that this code has anything to do with a Promise, but you should study the following structure:
class simplePromise{
constructor(resolveFn, rejectFn){
this.prop1 = 'prop1value'; this.prop2 = 'prop2value';
resolveFn.call(this, 'Passing to first function argument'); // calling in simplePromise context
rejectFn.call(this, 'Passing to second function argument');
}
}
const promise1 = new simplePromise(function(funcOneArg){
console.log(funcOneArg);;
console.log("this.prop1 = '"+this.prop1+"';"); // see why `.call(this` is in `constructor`
console.log("this.prop2 = '"+this.prop2+"';");
},
function(functionTwoArg){
console.log('-'.repeat(45));
console.log(functionTwoArg);
});
So when you pass the arrow function to the new simplePromise constructor, that entire function is the first argument (resolveFn). In order to instantiate this the way you want, you need to define the resolutionFunc and rejectionFunc and then do it like so:
const resolutionFunc = () => {do stuff}
const rejectionFunc = () => {do stuff}
const promise1 = new simplePromise(resolutionFunc, rejectionFunc)
I'm trying to have a Singleton instance of a dynamically imported module in my Next.js app. However, the current implementation I have seems to initialize a new instance every time I call getInstance.
Here's the implementation in gui.js:
let dynamicallyImportPackage = async () => {
let GUI;
await import('three/examples/jsm/libs/dat.gui.module.js')
.then(module => {
GUI = module.GUI
})
.catch(e => console.log(e))
return GUI;
}
let GUI = (function () {
let instance;
return {
getInstance: async () => {
if (!instance) {
let GUIModule = await dynamicallyImportPackage();
instance = new GUIModule();
}
return instance;
}
};
})();
export default GUI;
and I call it in an ES6 class function using GUI.getInstance().then(g => { ... })
I would normally use React Context API or Redux for this kind of shared state but for this case, I need it to be purely in ES6 JS and not React.
You need to cache the promise, not the instance, otherwise it will try to import the module (and instantiate another instance) again while the first is still loading and has not yet assigned the instance variable.
async function createInstance() {
const module = await import('three/examples/jsm/libs/dat.gui.module.js')
const { GUI } = module;
return new GUI();
}
let instancePromise = null;
export default function getInstance() {
if (!instancePromise) {
instancePromise = createInstance()
// .catch(e => {
// console.log(e); return ???;
// instancePromise = null; throw e;
// });
}
return instancePromise;
}
I am receiving a value from a function to pass to a method in a class. When I pass the value returned from the function in web.js, it returns undefined in my shopifyStore class. What could be the reason and how I could solve this ?
PS: Beginner with javascript
web.js
window.shopify.findName({}, function(items) {
var shopify = items;
console.log(items); //this returns the value
pushValue(items);
});
export function pushValue(items) { return items; }
component
import * as shopifyHelper from '/web.js';
class shopifyStore extends HTMLElement {
constructor() {
super();
}
getItemCount() {
console.log(shopifyHelper.pushValue()) // this returns undefined
}
}
You should promisify the callback of window.shopify.findName method. Rework your pushValue function:
export function pushValue(items) {
return new Promise((resolve, reject) => {
window.shopify.findName({}, items => resolve(items));
})
}
and call it like this:
async getItemCount() {
console.log(await shopifyHelper.pushValue());
}
or:
getItemCount() {
shopifyHelper.pushValue().then(items => console.log(items));
}
Here you can read more about async functions.
And here you can read more about promises.
I tried static methods in es6, any clue why I can't chain my static method like below? Is it even possible to chain 2 static methods?
//nameModel.js
const schema = new mongoose.Schema({ name: String })
class NameClass {
static async findAll() {
return this.find({})
}
}
schema.loadClass(NameClass)
export const model = initModel('NameSchema', schema)
//controller.js
import { model as NameModel } from '../models/nameModel'
export default () => async (req, res) {
try {
const test = await NameModel.findAll()
console.log('test', test) //have all the records
const response = await NameModel.findAll().sort('-name') // NameMode.sort is not a function
} catch (e) {
console.log(e)
}
}
What is the diffrence between static and non static method in mongoose schema? I'm confused as the doc only show code sample. I felt it's redundant as it doesn't show difference between two http://mongoosejs.com/docs/advanced_schemas.html
this in static method is refer to class function itself, since it defined as a method of a class.
class NameClass {
static async findAll() {
return this.find({})
}
}
is equal to:
class NameClass {}
NameClass.findAll = async function() {
return this.find({})
}
see MDN Classes
My await in initalize does not get called and I don't know why:
I have a React class that acts as the entry point:
export const HulaHoop = React.createClass({
async parseData(objects){
return await parser(objects)
}
});
which calls the parser function that creates instances of MyClass:
class MyClass {
constructor(name){
this.name = name;
}
async initialize(obj){
this.field = await getField(obj);
console.log(this.field) // <--- <Promise>
}
static async create(obj) {
const myInstance = new MyClass(obj);
await myInstance.initialize(obj);
return myInstance;
}
}
export async function getField(obj) {
const d2 = await getInstance();
let route = 'someField/' + obj.field;
return await d2.get(route);
}
export default async function parser(objects) {
let classes = [];
for (let obj of objects) {
let myclass = await MyClass.create(obj)
classes.push(myclass)
}
return classes
}
Since the (final) class creation happens asynchronously, there is a static async create() method in place to initialize the Class. Initalize can never finish because it's waiting for the Async callback. How can I resolve so that this.field is actually resolved?
Edit: it's the Jest testing that fails:
test('parsing', async () => {
let objects = await readCSV('file.csv');
let res = await parser(objects); // waits forever in MyClass.create(obj)
const expected = [ ... ]
expect(data).toEqual(res);
}