forkJoin never finishes even though complete() is called - javascript

I am trying to run a forkJoin, but it isn't running the pipe(). It runs the two methods in the array, but not the result, and I am not sure why...
in the onStart() is where I am calling forkJoin() but here never prints to the console. Am I missing a sub.complete() somewhere?
export MyClass extends ParentClass {
override onStart() {
super.onStart();
forkJoin([this.#addPhysics3D(), this.#addPhysics2D()]).pipe(
tap(() => {
console.log('here');
})
).subscribe();
}
#addPhysics3D() {
return new Observable<void>(sub => {
try {
import('#engine/physics').then(physics => {
if (!physics) return sub.complete();
if (Reflect.hasMetadata(physics.PHYSICS_RIGIDBODY, this.target)) {
const world = Injector.get(physics.World);
world?.add(this);
}
return sub.complete();
});
} catch (e) { sub.complete(); }
});
}
#addPhysics2D() {
return new Observable<void>(sub => {
try {
import('#engine/physics2d').then(physics => {
if (!physics) return sub.complete();
if (Reflect.hasMetadata(physics.PHYSICS2D_RIGIDBODY, this.target)) {
const world = Injector.get(physics.World2D);
world?.add(this);
}
return sub.complete();
});
} catch (e) { sub.complete(); }
});
}
}

Problem
Seems like, you never catch errors caused by import
Clearly #engine/physics2d doesn't exists in the current environment, but no console log appear:
try {
import ('#engine/physics2d').then(console.log)
} catch (error) {
console.log('error', error)
}
Solution
Use await to make promise throw an error
(async() => {
try {
await
import ('#engine/physics2d')
} catch (error) {
console.log('error', error)
}
})()
Use .catch on the promise
import ('#engine/physics2d').then(console.log).catch(error => console.log('error', error))
The last one will probably work better with your codebase

Related

How to capture Google recaptchaV3 Promise Timeout?

import canUseDOM from '#utils/dist/env/canUseDOM';
declare global {
interface Window {
grecaptcha: any;
}
}
export default async function getRecaptchaTokenExplicit(params: { recaptchaClientId: number }) {
return new Promise(resolve => {
if (canUseDOM && window.grecaptcha) {
const { recaptchaClientId } = params;
window.grecaptcha.ready(() => {
window.grecaptcha
.execute(recaptchaClientId, {
action: 'submit',
})
.then(function(token: string) {
return resolve(token);
});
});
} else {
return resolve('');
}
});
}
Calling await above function, I used to get Timeout console error from recaptcha (found it was because of badge element get removed due to component rendering), but in order to avoid it, how do I capture it and resolve return empty string?
error looks like this:
Since the error is in the promise, have you tried to .catch() it?
window.grecaptcha
.execute(recaptchaClientId, {
action: 'submit',
})
.then(function(token: string) {
resolve(token);
})
.catch(err => {
console.error(err);
resolve('');
});

TypeScript: Error inside Promise not being caught

I am developing a SPFx WebPart using TypeScript.
I have a function to get a team by name (get() returns also a promise):
public getTeamChannelByName(teamId: string, channelName: string) {
return new Promise<MicrosoftGraph.Channel>(async (resolve, reject) => {
this.context.msGraphClientFactory
.getClient()
.then((client: MSGraphClient) =>{
client
.api(`/teams/${teamId}/channels`)
.filter(`displayName eq '${channelName}'`)
.version("beta")
.get((error, response: any) => {
if ( response.value.length == 1) {
const channels: MicrosoftGraph.Channel[] = response.value;
resolve(channels[0]);
} else if (response.value.length < 1) {
reject(new Error("No team found with the configured name"));
} else {
reject(new Error("Error XY"));
}
});
})
.catch(error => {
console.log(error);
reject(error);
});
});
}
I call this function like this:
public getConversations(teamName: string, channelName: string, messageLimitTopics: number = 0, messageLimitResponses: number = 0) {
return new Promise<any>(async (resolve, reject) => {
try {
this.getTeamGroupByName(teamName)
.then((teamGroup: MicrosoftGraph.Group) => {
const teamId: string = teamGroup.id;
this.getTeamChannelByName(teamId, channelName)
.then((teamChannel: MicrosoftGraph.Channel) => {
const channelId: string = teamChannel.id;
this.getChannelTopicMessages(teamId, channelId, messageLimitTopics)
.then((messages: MicrosoftGraph.Message[]) => {
const numberOfMessages = messages.length;
... // omitted
});
});
});
} catch(error) {
reject(error);
}
});
}
And this getConversations() function itself is called from my webpart code:
public getConversations() {
if (this.props.teamName && this.props.teamName.length > 0 &&
this.props.channelName && this.props.channelName.length > 0) {
GraphService.getConversations(this.props.teamName, this.props.channelName, this.props.messageLimitTopics, this.props.messageLimitResponses)
.then((conversations) => {
.. // omitted
})
.catch((err: Error) => {
console.log(err);
this.setState({errorMessage: err.message});
});
} else {
// Mandatory settings are missing
this.setState({errorMessage: strings.MandatorySettingsMissing});
}
}
So, as you can see, above, I want to write out the error (message) I receive from the reject inside the getConversations() functions. The problem is, that I don't receive this rejection with the error, but in the console I see the following:
Uncaught Error: No team found with the configured name
I added the .catch() blocks you see above inside getTeamChannelByName() but this doesn't get hit during debugging.
Haven't worked much with promises and I am still confused a bit about them, so I guess that I probably have constructed the promise chain wrongly, maybe placed the catch block(s) in the wrong position(s)?
I had a look at common mistakes done with Promises and I certainly had what is called a "Promise Hell".
So, I am not 100% sure yet, but I guess, the result of this was that there was no catch block for my .then("Promise") so the rejection went to nowhere.
I have now changed the calling method to this, and I also removed the try/catch blocks, as it is unnecessary:
public getConversations(teamName: string, channelName: string, messageLimitTopics: number = 0, messageLimitResponses: number = 0) {
return new Promise<any>(async (resolve, reject) => {
let teamId: string = null;
let channelId: string = null;
this.getTeamGroupIdByName(teamName)
.then(tId => {
teamId = tId;
return this.getTeamChannelIdByName(teamId,channelName);
})
.then(cId => {
channelId = cId;
return this.getChannelTopicMessages(teamId, channelId, messageLimitTopics);
})
.catch(error => {
reject(error);
});
});
}

JS Get data from a constructor with a Promise

I'm unable to get the data response from unsplashAxios within the GetPhoto constructor, at the moment it just says 'Promise { }'. Have I missed something blatantly obvious or does this need rethinking?
Attempt 1
class Unsplash {
constructor(path) {
return new Promise((resolve, reject) => {
unsplashAxios(path)
.then(response => {
resolve(response);
})
.catch(error => {
reject(error);
});
});
}
}
class GetPhoto {
constructor() {
console.log('Get Photo');
const unsplash = new Unsplash('/photos/PgHc0Ka1E0A');
console.log(unsplash)
// Console.log Response - Promise { <pending> }
}
}
Attempt 2
class Unsplash {
constructor(path) {
return unsplashAxios(path)
}
}
class GetPhoto {
constructor() {
const unsplash = new Unsplash('/photos/PgHc0Ka1E0A');
unsplash.then((response) => {
console.log(response)
}).catch((response) => {
console.log(response);
});
}
}
Attempt 3 - After #Klaycon I've rewrote the above and this seems to work. But feedback would be great (good or bad).
const unsplashAxios = require('./unsplashAxios');
// Class
class Unsplash {
// Constructor
constructor() {
this.unsplash = null;
}
// Method
getPhoto(id){
this.unsplash = unsplashAxios( `/photos/${id}`);
this.unsplash.then((response) => {
console.log(response)
}).catch((response) => {
console.log(response);
});
}
// Method
getCollection(id){
this.unsplash = unsplashAxios(`/collections/${id}/photos`);
this.unsplash.then((response) => {
console.log(response)
}).catch((response) => {
console.log(response);
});
}
}
// Instance
myUnsplash = new Unsplash();
// Method
myUnsplash.getPhoto('PgHc0Ka1E0A');
// Method
myUnsplash.getCollection('62890');
You return a Promise.
As #Bergi said in your comment, avoid to use them in a constructor.
Use :
unsplash.then((data) => {
//do something with data
});
to wait til the promise is resolved. You can read more about promises here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Since unsplashAxios is already returning a promise, you don't need to wrap it in another promise (you're resolving unsplashAxios's promise then putting that result into another Promise that would have to be resolved elsewhere. Changing your code to this should work:
constructor(path) {
unsplashAxios(path)
.then(response => {
//do something with the response
})
.catch(error => {
//do something with the error
}
}
}
I ended up rewriting and found the following to work as expected:
class Unsplash {
constructor(path) {
return unsplashAxios(path)
}
}
class GetPhoto {
constructor() {
const unsplash = new Unsplash('/photos/PgHc0Ka1E0A');
unsplash.then((response) => {
console.log(response)
}).catch((response) => {
console.log(response);
});
}
}

Possible Unhandled Promise Rejection (id: 0) React Native AsyncStorage

I am promisifying the React Native AsyncStorage getItem method but I am being warned that it is returning a possible unhandled promise rejection. Here's what I'm doing, what's wrong with my code?
In App.js ComponentDidMount()
componentDidMount() {
ConnectyCube.init(...config);
authInitialization = async () => {
const locallyStoredPhoneNumber = await getStoredPhoneNumber();
console.log(locallyStoredPhoneNumber);
authorizeFirebase(this.getFirebaseAccessToken);
this.props.authorizing(true);
}
authInitialization();
}
Then in localStorage.js
export const getStoredPhoneNumber = () => {
return new Promise((resolve, reject) => {
AsyncStorage.getItem('#phone_number', (error, result) => {
result ? resolve(result) : reject(error);
})
})
}
Thanks in advance.
UPDATE
I have now added error handling:
export const getStoredPhoneNumber = () => {
return new Promise((resolve, reject) => {
AsyncStorage.getItem('#phone_number', (error, result) => {
result ? resolve(result) : reject(error);
})
}).catch(error => console.error(error))
}
Seems to work - here's my extra logic that depends on the result of the AsyncStorage call:
componentDidMount() {
ConnectyCube.init(...config);
authInitialization = async () => {
const locallyStoredPhoneNumber = await getStoredPhoneNumber();
locallyStoredPhoneNumber !== undefined
? authorizeFirebase(this.getFirebaseAccessToken) && this.props.authorizing(true)
: this.setState({ newUser: true })
}
authInitialization();
}
Seems like this should work:
async componentDidMount() {
ConnectyCube.init(...config);
try {
const locallyStoredPhoneNumber = await AsyncStorage.getItem('#phone_number');
locallyStoredPhoneNumber !== undefined
? authorizeFirebase(this.getFirebaseAccessToken) && this.props.authorizing(true)
: this.setState({ newUser: true })
} catch (e){
// handle error
}
}
One way to handle promise rejection would be to use try...catch block where your promise is being returned.
try{
const locallyStoredPhoneNumber = await getStoredPhoneNumber();
} catch(error){
//Error handling code here
}
You need to 'catch' any errors which might be thrown and handle them (otherwise React will complain):
componentDidMount() {
authInitialization = async () => {
try {
const locallyStoredPhoneNumber = await getStoredPhoneNumber();
...
} catch (e) {
console.log(e) //handle error }
}
authInitialization();
}
}

Swallowing an error in my observable and returning a cached value - all keep the subscription alive

Please save my sanity. I'm trying to do some error handling inside my observable. I've followed heaps of articles but it hasn't clicked. I'd rather handle errors inside the service and push out the cached data if an error occurs.
I do this inside my angular2 component...
private initializeJobPolling() {
this.subscription = Observable
.interval(5000)
.startWith(0)
.flatMap(() => {
return this.jobService.getJobs();
})
.subscribe(
(jobsContainer: any) => {
let allJobs: IJob[] = jobsContainer.jobs.map(j => new Job(j));
this.allJobs = allJobs;
} );
}
and inside jobService...
getJobs() {
let thisService = this;
return thisService.http.get('app/services/jobs.json')
.map((responseData) => {
console.log('getting Jobs from API');
thisService.allJobs = responseData.json();
return thisService.allJobs;
});
}
Use catch:
getJobs() {
return this.http.get('app/services/jobs.json')
.map((responseData) => {
console.log('getting Jobs from API');
this.allJobs = responseData.json();
return this.allJobs;
})
.catch(err => {
console.error(err);
return Observable.just(this.allJobs);
});
}
Update:
As pointed out in the comments, RxJS 5 doesn't have .just, so use .of instead:
getJobs() {
return this.http.get('app/services/jobs.json')
.map((responseData) => {
console.log('getting Jobs from API');
this.allJobs = responseData.json();
return this.allJobs;
})
.catch(err => {
console.error(err);
return Observable.of(this.allJobs);
});
}

Categories

Resources