Undefined value returned - Javascript - javascript

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.

Related

Returning async data from method1 to method2 while having no influence on the script that fetches the async data in method1

In TypeScript I try to call an external script SPCalendarPro in a private method that async fetches some data. The script is called as follows:
private _getSPCalendarPro() {
const spcalpro: any = require('./spcalendarpro.js');
}
After calling the script I need to call the function as follows:
spcalpro.getCalendarEvents({
listName: "StaffSchedule"
}).ready(function(data, obj) {
if (obj.error) console.error( obj.error );
console.table( data );
});
The complete method is as follows:
private _getSPCalendarPro() {
const spcalpro: any = require('./spcalendarpro.js');
return spcalpro.getCalendarEvents({
listName: "StaffSchedule"
}).ready(function(data, obj) {
if (obj.error) console.error( obj.error );
console.table( data );
return obj;
});
}
The script returns a data and obj variable, which I have to use in another method. However, when I call the above method from another function I get the .ready() function back as text. Leaving out the .ready() part returns me the fetched object, but with the data part empty. This is due to the fact that the data is fetched async and not yet resolved when the method is returned. The returned object consists of:
listName:
async:
fields:
userEnvData:
callback:
CamlQuery:
data:
The method from which I call the _getSPCalendarPro method:
private _calendarData() {
const calObj = this._getSPCalendarPro();
const calObjData = calObj['data'];
console.log(calObj);
console.log(calObjData);
}
calObj is filled nicely, but calObjData is undefined. I've tried to use async / await and jquery deferred, but without luck. I couldn't find an answer on this particular subject either. Hope someone can help me on what I am doing wrong.
Thanks.
EDIT
I have tried to create a promise for _getSPCalendarPro, but I am not sure how to do this the right way since the external script is fetching the actual data.
private _getSPCalendarPro(): Promise<void> {
const spcalpro: any = require('./spcalendarpro.js');
const spcal = spcalpro.getCalendarEvents({
listName: "StaffSchedule"
}).ready(function(data, obj) {
if (obj.error) console.error( obj.error );
console.table( data );
return obj;
});
return spcal().then((response) => {
return response;
})
}
FINAL WORKING CODE
Thanks to Yoshi.
export interface ISPCalendarPro {
data: any;
}
private _getSPCalendarPro(): Promise<ISPCalendarPro> {
const spcalpro: any = require('./spcalendarpro.js');
return new Promise((resolve, reject) => {
const spcal = spcalpro.getCalendarEvents({
listName: "StaffSchedule"
}).ready(function(data, obj) {
obj.error
? reject(obj.error) // reject on error
: resolve(obj); // or resolve
})
})
}
private _calendarData() {
this._getSPCalendarPro().then((calData) => {
console.log(calData);
});
}
I think you can return a promise and resolve it using the supplied ready callback.
Something like:
type Data = unknown; // you'll need to fill this in.
class Something
{
private _getSPCalendarPro(): Promise<Data> {
const spcalpro: any = require('./spcalendarpro.js');
// return a promise
return new Promise((resolve, reject) => {
// on ready
spcalpro.getCalendarEvents({listName: "StaffSchedule"}).ready(function(data, obj) {
obj.error
? reject(obj.error) // reject on error
: resolve(data); // or resolve
});
});
}
private async _calendarData() {
try {
// await the promise
const data = await this._getSPCalendarPro();
}
catch (error) {
console.error(error);
}
}
}

How to execute asynchronous functions in the constructor? [duplicate]

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

How to resolve a Promise only after a state change

I am trying to resolve a Promise only after a certain condition is met outside the promise:
class myClass extends ... {
render() {
...
this.fetch();
....
}
fetch(){
Promise.all([
....,
....,
....
]).then( () => {
// new Promise or somthing like that
// wait for state changed to resolve it
}
).then(
// do something
)
}
stateChanged(state){
if(condition){
// resolve promise from here
}
}
}
I tried to implement it in similar way to this question
Resolve Javascript Promise outside function scope
but I do not understand
how we can call a variable
how to make this work inside my Class
I tried it with a this.waitForPromise = null variable, but when I call
it later this.waitForPromise() I get a is not a function Type error.
class myClass extends ... {
render() {
...
this.fetch();
....
}
constructor(){
this._waitForPromise = null;
}
fetch(){
Promise.all([
....,
....,
....
]).then( () => {
new Promise(function(resolve, reject) {
this._waitForPromise = resolve;
});
}
).then(
// do something
)
}
stateChanged(state){
if(condition){
this._waitForPromise();
}
}
}
Any help and explanation is much appreciated.
You have a closure issue using this, so capture it first.
You should return the promise in your then.
You should check to see if promise is created when state changes.
Keep in mind you can only invoke one of resolve/reject, and only once.
class myClass extends ... {
render() {
...
this.fetch();
....
}
constructor(){
this.resolve = null;
this.state = null; // TODO initial state
}
fetch(){
const that = this;
Promise.all([
....,
....,
....
]).then( () => {
return new Promise(function(resolve, reject) {
that.resolve = resolve;
that.checkState();
});
}
).then(
// do something
)
}
checkState(){
if(condition && this.resolve){
this.resolve();
this.resolve = null; // set to null to ensure we don't call it again.
}
}
stateChanged(state){
this.state = state;
this.checkState();
}
}
OK, so I'm not all that familiar with Polymer 3, but I'm fluent in Polymer 1 and 2.
I'm getting that you want to do this:
Get an ajax promise.
Return said ajax promise once a certain state has changed.
do a fetch call if said promise hasn't been done.
What you need to do is to save your promise as a variable, and then just chain it together with other method calls. The comments below explains more about what I'm doing with your example code from the OP.
class myClass extends ... {
render() {
...
this.fetch();
....
}
fetch(){
this.set('savedPromise', Promise.all([ // save promise to variable
....,
....,
....
])
// not needed
/* .then( () => {
// new Promise or somthing like that
// wait for state changed to resolve it
}*/
).then(
// do something
return result // added this line
)
}
stateChanged(state){
if(condition){
if (this.savedPromise) {
this.savedPromise.then(function(response) {
// handle data
});
else {
fetch().then(function(response) { // if set before render(), do a fetch call.
// handle data
}
}
}
}
}

How to mock a node module with jest within a class?

I need to mock the DNS node module in a class but I am unsure how to do so as it is enclosed in the class. Here is a sample of what the class looks like...
import { lookup } from 'dns';
class Foo {
// ...
protected async _bar(IP: string) {
// I want to mock "lookup"
await new Promise<undefined>((resolve, reject) => {
lookup(IP, (err, addr) => {
if (err) reject(new Error('DNS Lookup failed for IP_ADDR ' + IP));
resolve();
});
});
// If dns found then return true
return true;
}
// ...
}
I would like to create a test file foo.spec.ts that contains a test similar to the following:
import { Foo } from './Foo';
describe('Foo', () => {
it('Bar Method returns true on success', () => {
const test = new Foo();
expect(test._bar('192.168.1.1')).resolves.toBeTruthy();
});
});
I am unsure how to mock the lookup call within the class Foo given that the class definition is in a separate file from the test itself.
Any help would be appreciated!
The way you are using lookup won't work since it doesn't return a Promise...
...but you can convert it to a version that does return a Promise by using util.promisify.
The code would end up looking something like this:
import { lookup as originalLookup } from 'dns'; // <= import original lookup...
import { promisify } from 'util';
const lookup = promisify(originalLookup); // <= ...and promisify it
export class Foo {
async _bar(IP: string) {
await lookup(IP).catch(err => { throw new Error('Failed'); });
return true;
}
}
You could then mock lookup in your test using jest.mock like this:
import { Foo } from './Foo';
jest.mock('dns', () => ({
lookup: (hostname, callback) => {
hostname === 'example.com' ? callback() : callback('error');
}
}))
describe('Foo', () => {
it('Bar Method returns true on success', async () => {
const test = new Foo();
await expect(test._bar('example.com')).resolves.toBeTruthy(); // Success!
await expect(test._bar('something else')).rejects.toThrowError('Failed'); // Success!
});
});
Note that the mock needs to be created using jest.mock (and not something like jest.spyOn) since calls to jest.mock get hoisted and run first. The mock needs to be in place before Foo.js is imported since the first thing it does is create and store the promisified lookup.
From jest's tutorial,
jest.mock('./sound-player', () => {
return function() {
return {playSoundFile: () => {}};
};
});
so you could do sth like jest.mock('dns', () => ({ ... }));

new object with async routines

I want to instantiate an object where the constructor performs async calls before returning. The purpose is to do async currying. I am using co. The below example fails. What am I doing wrong?
var co = require('co')
function asyncFunction() {
return new Promise(function (resolve) {
resolve()
})
}
function MyObject () {
co(function * () {
yield asyncFunction()
}).then(()=> {
this.runSomething = function() {
return 'something'
}
})
}
new MyObject().runSomething()
// TypeError: (intermediate value).runSomething is not a function
A new operator will always return an object immediately, and synchronously. You can't delay that operation.
You could instead create a function that will return a Promise for your object.
function makeObjectAsync() {
const asyncResult = asyncOperation();
return asyncResult.then(result => ({
runSomething() { /* ... */ }
}));
}
myObjectAsync()
.then(obj => {
// obj is ready to use.
return obj.runSomething();
});
You can combine that with co fairly easily to get read of some of the .then()s.

Categories

Resources