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('');
});
Related
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
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);
});
});
}
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);
});
}
}
I am not sure why my function is giving me this error. In the function returning the value, I ensured there is a return statement on all levels. Could I have a second opinion as to why this error may be occurring? The service returns the data perfectly, the front end, however, is not seeing the return from my action.
I tried other solutions with the same title, so hopefully, this is not a duplicate
Front end:
componentDidMount() {
const { item } = this.props;
this.props.getDetails(Number)
.then((res) => {
this.setState({
data: res.response.response.Results
})
});
}
mapping:
function mapDispatchToProps(dispatch) {
return {
getDetails(Number) {
dispatch(getActions.getDetails(Number));
}
};
}
function retrieving:
function getDetails(Number) {
return (dispatch) => {
dispatch({ type: policyConstants.ITEM_GETDETAIL_REQUEST });
return Service.getDetails(Number)
.then(
response => {
dispatch({ type: Constants.ITEM_GETDETAIL_SUCCESS });
var pDetails = response.response.Results;
return { pDetails };
},
error => {
dispatch({ type: policyConstants.POLICY_GETDETAIL_FAILURE, error});
}
);
}
}
this.props.getDetails(Number) in your componentDidMount is evaluated to undefined because getDetails in your mapDispatchToProps returns nothing.
Change like this :
function mapDispatchToProps(dispatch) {
return {
getDetails(Number) {
return dispatch(getActions.getDetails(Number));
}
};
}
Under my Angular 6 app, I have a variable "permittedPefs" which is getting value after an HTTP call (asynchronous)
#Injectable()
export class FeaturesLoadPermissionsService {
permittedPefs = [];
constructor() {
this.loadUserPefsService.getUserRolePefs(roleId)
.subscribe(
(returnedListPefs) => {
this.permittedPefs = returnedListPefs;
},
error => {
console.log(error);
});
}
}
in another method, I'm using that same variable:permittedPefs
But as it's initially empty and it gots its value after such a time, so I need to wait for it to re-use it.
I've tried to use async-await, and my purpose is waiting for permittedPefs to got aobject value
async checkPefPresence(pefId) {
const listPefs = await this.permittedPefs
}
how to fix it ??
As loadUserPefsService.getUserRolePefs method returns Observable you can store it and subscribe to it later when you need it.
#Injectable()
export class FeaturesLoadPermissionsService {
permittedPefs = [];
constructor() {
this.userRolePefsObservable = this.loadUserPefsService.getUserRolePefs(roleId);
}
}
checkPefPresence(pefId) {
let listPefs;
this.userRolePefsObservable.subscribe(
(returnedListPefs) => {
listPefs = returnedListPefs;
},
error => {
console.log(error);
});
}
Use a behaviorSubject
#Injectable()
export class FeaturesLoadPermissionsService {
permittedPefs: BehaviorSubject<any[]> = new BehaviorSubject([]);
constructor() {
this.loadUserPefsService.getUserRolePefs(roleId)
.subscribe((returnedListPefs) => {
this.permittedPefs.next(returnedListPefs);
},
error => {
console.log(error);
});
}
}
Then where ever you are checking for it(remember to unsubscribe when you are done)
if it has to be async you can do it like below
async checkPefPresence(pefId): Promise {
return new Promise((resolve, reject) => {
this.featuresLoadPermissionsService.permittedPefs.subscribe(listPefs => {
//handle whatever check you want to do here
resolve();
},reject);
})