Trouble with axios returning promise object - javascript

I imagine it's something really simple, but I'm having some trouble getting the correct response. My code is returning the promise object, and not the value.
My axios call is something like this:
export const myFunc = async (hash: string) => {
return axios.get(`${url}/${path}?hash=hash`)
.then((response: any) => {
console.log('my response: ', response.data) // {key: value} as expected
return response.data
})
}
I call it from another file
const xy = async (c: string) => {
return myFunc(c)
}
console.log('result of xy(): ' xy('some hash')) // result of xy(): { Promise <pending> } <--- ????
If I .toString() it, because I'm annoyed (and I think I had some reason why at one point but I don't remember what that is), I get
result of xy(): [object Promise]
I've googled, I've stack overflowed, and now I'm asking the question because what I've found so far doesn't quite work.
Thanks for you help

Explicit promise syntax fixed the issue. I'm sure I was missing something really simple. Thanks much to #EmileBergeron. But we also decided that the data didn't need to be encrypted at rest, so by storing this non sensitive data in an unencrypted state, we no longer needed to make a separate rest call to un-encrypt the hash and didn't need to worry about working in an additional promise in the first place.

Related

Can you intercept and create a AxiosPromise within a function?

I want to change a function that returns an AxiosPromise that currently works like so:
example(){
return api.get(url);
}
Where api.get returns an object of type AxiosPromise<any>
To something like this, where I can modify the object the original promise resolves:
example(){
if(condition){
newPromise = new AxiosPromise<any>((res, rej) => {
api.get(url).then( x => {
x.data = 3;
res(x);
}
};
return newPromise;
} else return api.get(url);
}
This example is based on my knowledge on how you use the normal Promise object but from what I can see AxiosPromise can't be used in this way.
Goal is to be able to modify the response if the condition is true but not have to refactor everywhere the function is currently being used.
My suggestion isn't wholly dissimilar to what you're already doing. You're using a regular promise now and my solution involves it as well. The unforeseen side effects you mention aren't likely to spring from the type of promise you return, but rather from what that promise resolves to i.e. (axios response object). So long as you keep it intact, I doubt all those other calls to your function will have any issues. As such, here is how I would modify your example function. In its original it looks like so:
example(){
return api.get(url);
}
And here is how you call it:
example().then(({data}) => console.log(data))
Ok. So far so good, but for starters I would wrap your axios call into a regular promise:
example(){
return Promise.resolve(axios.get(url).then(response => response))
}
This change does not change a thing on the caller side. You would still call it like so:
example().then(({data}) => console.log(data))
Now you want to add a condition to the function that, if true, modifies the response in some way. So here it is:
example(condition){
return Promise.resolve(axios.get(url).then(response => {
if(condition === true) response.data.somekey = 'Changed';
return response; //The response object is intact save for the one change.
}))
}

Issue using Axios with Scryfall API

Attempting to use axios to make a get request at the following endpoint, and I keep getting errors:
When I check it using Postman and in a browser (GET request), it returns data just fine, but otherwise I can’t get a response.
This is the call I’m using, I don’t know if it’s some sort of issue with my code or with axios itself:
axios.get(`https://api.scryfall.com/cards/named?exact=${args.name}`)
.then((res) => {
console.log(JSON.stringify(res));
})
.catch((err) => {
if (err.response) {
throw new Error(`Card with name (${name}) not found!`)
}
throw new Error(`Could not complete that query!`)
})
The argument args.name is passed as part of a GraphQL resolver, and it definitely has a value, so not sure what the deal is.
Any help would be greatly appreciated!
There are a couple of problems here.
Generally it's not a good idea to throw new errors after catching the original error. As written, your code throws an additional error because the axios Promise is thrown again instead of being dealt with inside catch - so node will complain that you didn't resolve or reject the promise.
The substantive issue is the same as the one answered here except for res - the actual error is TypeError: Converting circular structure to JSON which is caused by trying to JSON.stringify the res object:
JSON doesn't accept circular objects - objects which reference themselves. JSON.stringify() will throw an error if it comes across one of these.
The request (req) object is circular by nature - Node does that.
In this case, because you just need to log it to the console, you can use the console's native stringifying and avoid using JSON
So you can fix this by changing your code to:
axios.get(`https://api.scryfall.com/cards/named?exact=${args.name}`)
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err)
if (err.response) {
console.error(`Card with name (${name}) not found!`)
} else {
console.error(`Could not complete that query!`)
}
})
If you were just using console.log for testing/as an example and actually need to stringify the data to use some other way, just make sure you're stringifying the data (which is presumably what you actually want) and not the whole res object:
axios.get(`https://api.scryfall.com/cards/named?exact=${args.name}`)
.then((res) => {
let scryfallData = JSON.stringify(res.data)
doSomethingWith(scryfallData)
})

data type of value returned from Promise or async / await

i know there's a bunch of related questions / posts regarding this question but they don't really answer my question, my question here is simple, if I have a promise and i wrapped it in an async function and await it til it's settled like this:
async function getUserData() {
return fetch('url'); // fetch returns a promise, wrapping the user data returned
}
async main() {
const user = await getUserData();
// what is the type of the user object here?
}
I'm asking this question because I'm using TypeScript, I usually try to type cast the return value to the expected type like this:
async function getUserData() {
return fetch('url') as UserInfo;
}
say UserInfo has a name attribute but if I try write in this way then user.name is undefined:
async function main() {
const user = await getUserData();
console.log(user.name); // undefined.
}
it makes me how should I 'unwrap' a promise with a value in it?
You can't really know at compile time what it is, because this is explicitly something that happens at runtime.
So usually the result of a JSON.parse() is any. A stricter version could be the unknown type.
If you want to just assume whatever you got back is going to be the right type, without validating (because you trust the server), I think I would do that as such:
async function getUserData(): Promise<UserInfo> {
return fetch('url'); // fetch returns a promise, wrapping the user data returned
}
I could be wrong, but I think the way that Async/Await works is that it wraps the function in a native promise.
I tested out your examples in the browser console and both functions return a Promise.
Promise {<pending>}
I am not sure about what that is cast to in Typescript as I don't use it. But if you drop into the browser console you can test all of this. It functions as a pretty good REPL.
As a function of design, the getUserData() function does not need to be async as you are not awaiting anything in it.
As for unwrapping it, you can use the fetch API since you now have a fetch result:
const data = getUserData();
data.then(response => response.json())

Promise troubles with pnp/sp

I am building an application customiser in SPFX and I am using pnp/sp to get data from a Sharepoint list - all easy so far. I have figured out the code like this, but it is just returning [object promise] here is my code , any help would be brilliant.
I am calling the function like this :
public emailAddressGetter = this.GetSharePointData();
I am trying to show the output like this :
${escape(this.emailAddressGetter.toString())}
and this is the promise I am executing :
private async GetSharePointData(): Promise<any>
{
let myVar : string;
var resultData: any = await sp.web.lists
.getByTitle('Emails')
.items
.select('EmailAddress')
.getById(99)
.get().then((r => {
myVar = r.EmailAddress;
}));
console.log(myVar);
return myVar;
}
any help would be appreciated, I know I am almost there :) thanks guys
I think your GetSharePointData returns a Promise, because it has async declaration, so you need to execute code asynchronously and wait for the result.
Instead of:
public emailAddressGetter = this.GetSharePointData();
${escape(this.emailAddressGetter.toString())}
Try:
this.GetSharePointData()
.then(res => {
// res here is myVar
${escape(res.toString())};
});
Firstly fix your code's type annotations. You are completely defeating the point of TypeScript by suppressing the errors the language exists to catch by specifying vacuous types instead of leveraging inference. This isn't Java.
async GetSharePointData() { // return type is inferred as `Promise<string>`
const result = await sp.web.lists // the `any` you had here was worse than useless.
.getByTitle('Emails')
.items
.select('EmailAddress')
.getById(99)
.get();
const emailAddress= result.emailAddress;
console.log(emailAddress);
return emailAddress;
}
Now onto async functions and promises. An async function or method always returns a promise. Assigning the result of calling such a function directly to a property or variable will always result in the behavior you described
GetSharePointData().toString() === "[object Promise]"
The correct approach to setting the property emailAddressGetter (BTW that's a terrible name for that property either way) to the email address that the promise eventually resolves with depends on the context, but here is something you might do.
constructor() {
this.emailAddressPromise = this.GetSharePointData();
this.emailAddressPromise.then(emailAddress => this.emailAddress = emailAddress);
}
But that could be awful and unnecessary unpredictable depending on what you are trying to do.

angular2 : issue with rest api

I am trying to learn angular2 (coming from php scripts, it is quite difficult^^), using a real rest api in "Tour of Heroes".
From what I read, I thought it could be simple...
I have a working api, built with Express :
curl -XGET http://localhost:3001/heroes
[{"_id":"58185c8a8af4b512c51c0519","no":"1","name":"bahamut","__v":0,"updated_at":"2016-11-01T09:12:42.803Z"},{"_id":"58185ca78af4b512c51c051a","no":"2","name":"gatz","__v":0,"updated_at":"2016-11-01T09:13:11.063Z"},{"_id":"58185ec98af4b512c51c051b","no":"3","nam...
In hero.service.ts, I can get the data :
getHeroes(): Promise<Hero[]> { // {{{
console.log('getheroes in service');
console.log( "%o", this.http.get(this.heroesUrl).toPromise());
return this.http.get(this.heroesUrl)
.toPromise()
.then(response => response.json().data as Hero[])
.catch(this.handleError);
} // }}}
console screenshot
When I do same console.log on original "Tour of Heroes", I have an array in data whereas here I have a string...
I guess I have to convert somewhere the string but whatever I tried, it does not work.
(I read many examples with Observable too but I have not succeeded either)
Help wanted to explain me how to...
TIA
JP
You're so close! The mistake here is in how you handle Promises, along with misunderstanding how they return. In this case you were trying to assign an undefined property (response.json().data) when you meant to coerce response.json() as type Hero[] then return.
What you'll need to do is ensure you have a matching type to assign your response when it's converted to a JSON with that json() call. The Hero type in the guide does not match your response, and you'll have errors because of that.
To check that you're receiving a response, make a call to the service's getHeroes() function and log the returned value. Logging internally to the function can be done, but that would be where understanding how Promises work in depth would help more than anything.
getHeroes(): Promise<Hero[]> {
return this.http.get(this.heroesUrl)
.toPromise()
.then((response) => response.json() as Hero[])
.catch(this.handleError);
}
If you want to know I'm not crazy, here's the code to log internally. This should log your response no matter what type is received.
getHeroes(): Promise<Hero[]> {
return this.http.get(this.heroesUrl)
.toPromise()
.then((response) => {
console.log(response.json());
})
.catch(this.handleError);
}
For further context into why you do these then() calls, a Promise returns asynchronously which means the result is only available after an indeterminate amount of time has passed. Any execution that must wait for the result must either happen in the then() calls or after the function returns an actual value. As JS functions run synchronously if you try to execute the following example you'll see a printout of undefined rather than a string response. This is because the console.log(r) is called immediately after the promise call, completely oblivious to the fact it hasn't actually let r be assigned a value.
getHeroes(): Promise<Hero[]> {
var r;
this.http.get(this.heroesUrl)
.toPromise()
.then((response) => r = response.json())
.catch(this.handleError);
console.log(r);
}

Categories