Very easy question. When I run console.log on the jsonPayload in line 6, I see the expected output. When I run console.log again on jsonPayload in the last line, it returns an empty {}. How do I access the payload outside of the initial request?
var jsonPayload = {}
axios.get('http://localhost:8080/api/tools')
.then(function (response) {
jsonPayload = response.data[0]
// this returns the expected payload
console.log(jsonPayload)
})
.catch(function (error) {
console.log(error)
})
// this returns empty {}
console.log(jsonPayload)
I am not sure why you would like to access the jsonPayload outside of the axios get call, but I do know why you are receiving a log of {} from the outside console.log().
axios.get()
The above method will return a promise. This promise allows you to protect your next process from receiving empty data.
Inside the .then() method, you are able to retrieve the data that is sent back to you from the axios.get() call. However, this data cannot reach you until the process of retrieving data from the API, is complete.
This is where you are running into your issue. The console.log() outside of the .then() method is triggering before the completion of the ajax call.
You could also look into using ES7 async and await. This allows you to write these axios calls without the .then().
You cannot do this because the request is processed asynchronously.
As #d3L answered, you're limited to handle the response within the callback passed to .then. However, after ES8 you can use async / await which is an alternative to using typical Promise handling with callbacks. Is still asynchronous, but looks synchronous:
(async function() {
try {
const response = await axios.get('http://localhost:8080/api/tools');
console.log(response.data[0])
} catch(err) {
console.log(err);
}
})();
Note whether you're running it on node or the browser, the engine might not support it, so you might need to transpile the code using babel.
Related
This question already has answers here:
Async function returning promise, instead of value
(3 answers)
Closed 7 months ago.
please check the below axios get request example to a random public api.
Intro
I would simply like to create a function which will execute get requests and return data, so I can store the returned data in a variable and use it in the UI I am building.
Currently the function returns Promise object and not data as expected.
I also noticed that returned promise object is printed before logged data response. This makes me think that for some reason return command doesn't await the resolution of the promise.
I can use any other library apart axios as well, but tried fetch which is giving me same results.
Furthermore, I tried executing correct code from other stackoverflow answers on this topic, but it gave me same results.
Which leads me thinking my issue has something to do with my environment.
What I already tried
I looked into all answers on query "axios request returns undefined" and similar
I looked into all answers on query "get data from axios request" and similar
tried executing the code from browser using react
changing the function to synchronous, which instead of Promise object return undefined
My env:
System Version: macOS Monterey 12.4 (21F79)
Kernel Version: Darwin 21.5.0
Node#v18.7.0
axios#0.21.4
iterm#2
Example
The example can be executed by running node script.js.
// filename: script.js
const axios = require("axios");
async function fetchApi() {
const getRequest = await axios.get("https://api.publicapis.org/entries")
.then((response) => {
console.log("logged response:", response.data.count);
return response.data.count;
})
};
const response = fetchApi();
console.log("returned response:", response);
// execute#bash: node script.js
// returned response: Promise { <pending> }
// logged response: 1425
My question
I would expect returned result and logged result to be the same.
Please help me troubleshoot this issue.
It is impossible to return the data, because the data doesn't exist yet. Instead, you will need to return a promise. async functions automatically return promises, so you're part way there, but you currently aren't returning anything from your async function. I also recommend you don't mix async/await with .then as it will just cause confusion:
async function fetchApi() {
const response = await axios.get("https://api.publicapis.org/entries")
return response.data.count;
};
To access the eventual value of the promise, you will need to put your code in an async function and await the promise:
async function someFunction() {
const count = await fetchApi();
console.log(count);
}
Your fetchApi is an async function, which means it will return a Promise.
You should use either await or .then() syntax to execute your next actions after the Promise is resolved.
The only problem is that you can't use await syntax in your current implementation since you're using the result in the top level of your script file and not inside a function, Node didn't support top-level awaits in older versions.
So you should either use .then() syntax like this:
fetchApi().then( response => {
console.log("returned response:", response)
};
or wrap your code inside a function if you want to use await syntax:
async function main() {
const response = await fetchApi();
console.log("returned response:", response);
}
main();
if you are using Node v14.8+ you can just use await inside a module:
const response = await fetchApi();
console.log("returned response:", response);
In Tips & Tricks the docs say
Note: If a Node.js background function returns a Promise, Cloud Functions ensures that the Promise is settled before terminating.
My question is, does this ensurance extend to anonymous callbacks that are returned from a non-promised method?
export const mutateData = functions.https.onCall(async (data, context)=>{
const data = await getAndMutateData();
await Promise.all([
furtherMutate(data.data1),
furtherMutate(data.data2),
furtherMutate(data.data3)
]);
const addendum = {
addedData1: foo,
addedData2: bar,
addedData3: foobar
};
// Not awaited, we want to do this in the background while the client gets the status back
data.writeCallback(addendum);
return "success";
}
async function getAndMutateData(){
let [data1, data2, data3] = await Promise.all([firestoreGetter1(), firestoreGetter2(), firestoreGetter3()]);
mutate(data1);
mutate(data2);
mutate(data3);
return {
data1: data1,
data2: data2,
data3: data3,
writeCallback: async function(addendum): Promise<any>{
return Promise.all([
firestoreSetter1(data1, addendum.addedData1), // Merges data streams
firestoreSetter2(data2, addendum.addedData2),
firestoreSetter3(data3, addendum.addedData3)
]);
}
}
}
Will the cloud instance close before the writeCallback has had a chance to finish? I want to give the client the success/fail status without waiting for the firestore writes to complete. There are other failsafes in place for that case.
Firstly, the tip you cite applies only to background functions. The callable function you show here is not a background function. HTTP and callables are synchronous with the client that invokes them.
Will the cloud instance close before the writeCallback has had a chance to finish?
Yes, it will. You will need to await it if you want the function to complete before the function shuts down forcibly. You can't leave async work "dangling" when the function terminates, otherwise it might have strange behavior, or simply not work at all.
For callable functions, you are obliged to return a promise that becomes fulfilled with the data to send to the client, only after all the async work is complete. If you are trying to allow some work to continue after the client receives its response, you should refer to these:
Continue execution after sending response (Cloud Functions for Firebase)
Call cloud functions without waiting for response
The most common way of continue work after sending a response is to write a pubsub function to perform the extra work, then send a message to it from the main function.
I need to request data from my REST server to populate my UI (frontend). In doing so, I need to request some data from my and other servers. One such request is to get a list of states (provinces). I use fetch() and .json() to do this.
I see there are a few questions on SO addressing this issue mentioned below (here, here and a few more), however attempting their solutions/suggestions did not solve my problem.
e.g.
fetch("http://localhost:3443/app/location/provinces").then(e => e.json()).then(console.log);
Naturally fetch() being a network operation returns a promise, or I can use it with await & async. Also, .json() is also a promise. Since then() is called by resolve(data) to proceed to the next then() and .catch(error) processes reject(error), the request above should make sense.
Problem:
However, on each call to:
fetch("http://localhost:3443/app/location/provinces")
.then(e => e.json())
.then(console.log)
.catch(console.warn);
I get a Promise {<pending>}. Since it is the case, I tried the same with an async/await impl and receive the same "error":
(async () => {
let l = await fetch("http://localhost:3443/app/location/provinces");
console.log(l);
})();
What am I doing wrong/missing?
This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 3 years ago.
I have some code that authenticates by posting an object using npm request.
After posting the JSON object, a JSON response is returned which contains an authn token I can use in future GET/POST request headers.
I have some async code that returns the correct authn token but I can only access it via the .then function code block.
I have read through the usual linked thread here: How do I return the response from an asynchronous call? but even though the return result is in the .then function I still get undefined when trying to do anything other than console.log().
const postData = {
"auth": {
"username": "username",
"password":"password"
}
};
var returnRequest = () => {
var options = {
method: 'POST',
url: 'https://api.appnexus.com/auth',
body: postData,
json: true
};
return new Promise(async (resolve, reject) => {
await requestAPI(options, function (error, response, body) {
if (error) {
reject(error);
}
resolve(body);
});
})
}
var returnedResult
returnRequest()
.then((result) => {
returnedResult = result.response.token
})
.catch((error) => {
console.log(error);
})
console.log(returnedResult)
I would expect to see the returnedResult store the token as I understand it, the .then promise only runs one the request has happened?
A developer said I have to build all subsequent code inside the .then block but that sounds crazy, that I have to have my whole program inside this returnRequest function rather than be able to pass the returned token back outside to a global variable?
Is that the correct way to do it, and am I supposed to just build all subsequent requests using the result.response.token inside the
returnRequest()
.then((result) => {
returnedResult = result.response.token
})
function?
.then is the mechanism that promises use to let you know when the value is available. The "when" part is important: only the promise object knows what time your code should run at. So even if you try to write some extra code to store values in variables, the question of when it's safe to try to get those variables can only be answered by the promise's .then method.
So yes, any code that needs the values to be available needs to be put in the .then of the promise. Maybe you have some separate part of the codebase that needs to interact with the result, and so it feels clumsy to try to have to copy that code over to here. Well you don't need to: you just need to pass that other code the promise, and then that other code can call .then on the promise itself. For example:
const tokenPromise = returnRequest()
.then(result => result.response.token);
// Anywhere else that tokenPromise in scope can write whatever code it needs to:
tokenPromise.then(token => {
// Do anything with the token
});
// And a completely different piece of code can write its own stuff with the token
tokenPromise.then(token => {
// Do other stuff with the token
});
No you don't need to use result.response.token everywhere to use the authn token.
The thing here to understand is the flow of code. Your console.log statement may be returning you undefined .
Why ? Haven't you updated the global variable inside the then block of promise ?
Yes you have ! But the problem is that it is not reflected to you in the console log statement because this very statement is executed before any updation in the global variable.
So, it gets updated but it takes time to do so.
This is what is known as asynchronous code .
Now what about the suggestion of wrapping the code inside the .then block.
If you will add a console log statement beneath the updation (inside the then block) it will print you the exact token you are looking for.
But actually you don't need that , you can use aysnc/ await syntax to make it look like synchronus code, and then it will don't confuse you.
For example you can do something like this.
let result = await returnRequest();
let returnedToken =result.response.token;
// Now it will print you the token
console.log(returnedToken)
Make sure to add the async keyword infront of the function using await.
there are several ways to do what you ask, one way would be to wrap your entire code in async iife (immediately invoked function expression) so that you can use await.
!async function(){
....
....
....
var returnedResult = await returnRequest()
.then((result) => {
return result.response.token;
})
.catch((error) => {
console.log(error);
})
//continue
}()
I’ll try and answer parts of this question.
The setting of value for global variable inside of the .then callback is correct and you’ll have the value inside the “then” block. You can console.log inside of it and check.
The console.log outside in the “global” scope runs even before the the promise is resolved. Remember that java script is even driven. It registers the api call and continues executing the next line of it can. Which is why you’ll see an undefined value of token.
If all your subsequent requests depend on the auth token and you need to call some other API in the same call, you’ll have to do it in the .then call or create a promise chain with multiple .then which is essentially the main benefit of Promises. Previous result is passed on to the next promise.
I have a file where I code my whole connection with the REST service, and it works.
From another file, I am executing the following lines (everything works)
this.baseService.getCars(ID)
.subscribe(cars=> this.cars= cars);
To access to the values of the response I was using HTML. For example: *ngIf="cars"
Now, I would like to access by Javascript to the variable doing this:
this.baseService.getCars(ID)
.subscribe(cars=> this.cars= cars);
console.log(this.cars)
but I get undefined but I can access by HTML. I know that it is a stu**d question, but how should I do it? Which variable does contain the variable?
The execution order of those lines of code is not what you think it is.
To see cars in console, change your function to this:
this.baseService.getCars(ID)
.subscribe(cars=>{
this.cars= cars;
console.log(this.cars);
});
You need to place the console.log inside subscribe
this.baseService.getCars(ID)
.subscribe(
cars=> {
this.cars= cars;
console.log(this.cars);
},
error => {
console.log(error);
}
);
Subscribe is asynchronous, like a Promise, but isn't a Promise so, when you execute the code, the subscribe is fired, then the console log. But When the console.log is executing, subscribe is running yet, so that's why you get undefined.
You can do the console.log inside the callback function in subscribe
this.baseService
.getCars(ID)
.subscribe(cars=> {
this.cars = cars
console.log(this.cars)
});
Another solution is to use async/await. You can't use async/await directly with subscribe, because IT'S NOT A PROMISE. Fortunately Observers can be converted to a Promise.
So, in you service you can return a promise, like this:
getCars() {
// your service stuff
return this.api.get(url).toPromise().then( res => res.data); // This is the important part.
}
Then, in your component, call it with async/await:
async yourFunction() {
this.cars = await this.baseService.getCars(ID);
console.log(this.cars);
}
Now you can log this.cars after the getCars()
Hope this helps you.