I have a simple class
class A{
constructor(){
this.loadComponents().then(function(values) {callbackOnLoad();});
}
callbackOnLoad(){
//do some things
}
loadComponents(){
...
return Promise.all([p1,p2,p3,p4,p5,p6,p7,p8]);
}
}
I am unable to call callbackOnLoad after all promises are fulfilled. I know that "this" depends on the caller and so I understand why callbackOnLoad does not work. How can I solve this problem? How do I have to structure/design my code?
The proper way to do it would be calling then & catch immediately after Promise.all.
class A{
constructor() {
this.loadComponents();
}
callbackOnLoad = () => {
//do some things
}
loadComponents = () => {
return Promise.all([p1,p2,p3,p4,p5,p6,p7,p8]).then((values) => {
this.callbackOnLoad();
}).catch((error) => {
console.log(error);
});
}
}
Related
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 have a problem in my code where many entities have async loading procedures and can't be used until these are complete. There are chains of these dependencies.
So A->B->C where A needs B needs C.
I have written code like
class B{
constructor(callback){
this.loaded=false
this.load(callback)
}
load(callback){
...do stuff
this.loaded=true
callback()
}
}
class A{
constructor(){
this.loaded=false
this.b=new B(()=>{this.loaded=true})
}
}
This seems really bad. Can anyone suggest a better solution?
Usually, it is a bad practice to perform async task directly in constructor (as stated here). With this taken in account, you can follow VLAZ advice and start using promises.
You would then have something like this:
class B {
constructor() {
this.loaded = false
}
load() {
return new Promise((resolve => {
this.loaded = true;
// do stuff
return resolve()
}))
}
}
class A {
constructor() {
this.loaded = false
}
load() {
return new Promise(resolve => {
this.b = new B()
this.loaded = true
return resolve(this.b)
})
}
}
// Use it like this
const a = new A()
a.load()
.then(b => b.load())
.then(/* and so on */)
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
}
}
}
}
}
I have a Nestjs rest server with a controller and a service.
In my controller, there is the get function, when someone makes a get request:
#Get()
getAllFoos() {
return this.fooService.getAllFoos();
}
In my service, there is this function to get the documents from a database
async getAllFoos(): Promise<foos[]> {
try {
return await this.fooModel.find().exec();
} catch(e) {
return e;
}
This works!
I now need to change this to make it work with observables.
I changed the controller to:
#Get()
getAllFoos() {
this.fooService.getAllFoos().subscribe(
response => {
console.log(response);
},
error => {
console.log(error);
},
() => {
console.log('completed');
});
}
And the service to this:
getAllFoos(): Observable<foos[]> {
try {
this.fooModel.find().exec();
} catch(e) {
return e;
}
}
The error I get is
[Nest] 7120 - 2019-2-20 15:29:51 [ExceptionsHandler] Cannot read property 'subscribe' of undefined +4126ms
The error comes from
this.fooService.getAllFoos().subscribe(
this line from the controller. I really have no clue, what to change to make it work now.
Any help or idea is appreciated!
A Promise can not be cast as Observable. Create an observable with Observable.from() method (docs).
getAllFoos(): Observable<foos[]> {
return Observable.from(this.fooModel.find().exec());
}
rxjs versions < 6:
getAllFoos(): Observable<foos[]> {
return Observable.fromPromise(this.fooModel.find().exec());
}
I have written a generic method like this:
getArticleById(loading: Loading): void {
this.articleService.getArticleById(this.data.definition.id)
.map((res: any) => res.json())
.subscribe((res: any) => {
if (res.definition.is_purchased) {
//more code
} else {
//more code
}
loading.dismiss();
} else {
loading.dismiss();
}
}, () => { loading.dismiss(); });
}
Parent method (or calling) is like this:
myParentMethod(){
const loading = this.loader.create({
content: 'loading...'
});
loading.present();
this.getArticleById(loading);//can I call the `loading.dismiss()` here.
}
I would like to remove the loading parameter from the genric method(getArticleById()) and need to put that inside the parent method(myParentMethod()) after resolving the subscription.Can you tell me how to do that?
To handle the observable terminating at the higher level, you'll need to return an observable that the higher-level function can access.
Try writing it like this:
getArticleById(loading: Loading) {
const articles$ = this.articleService.getArticleById(this.data.definition.id)
.map((res: any) => res.json());
article$.subscribe((res: any) => {
if (res.definition.is_purchased) {
//more code
} else {
//more code
}
});
return article$;
}
finally is a useful operator which
Invokes a specified action after the source observable sequence terminates gracefully or exceptionally.
myParentMethod(){
const loading = this.loader.create({
content: 'loading...'
});
loading.present();
this.getArticleById().finally(() => loading.dismiss());
}
However, this code is still structured a bit awkwardly. I'd separate out the logic to get the observable from that to handle it, and write the code as follows:
getArticleById(): Observable<Article> {
return this.articleService.getArticleById(this.data.definition.id)
.map(res => res.json());
}
handleArticle(article) {
if (article.definition.is_purchased) {
//more code
} else {
//more code
}
}
myParentMethod(){
const loading = this.loader.create({
content: 'loading...'
});
const article$ = this.getArticleById();
loading.present();
article$
.finally(() => loading.dismiss())
.subscribe(article => this.handleArticle(article));
}