I have a fetch instruction in one function that grabs an API key from a server and it's used by a few other objects to deliver that API key to whatever service needs it.
export default async function getAPIKey(key) {
return await (await fetch('http://localhost:8000/' + key)).json();
}
And in my weather object:
export default {
URI: 'https://api.openweathermap.org',
getLocalWeather: async function(city=null, countryCode=null) {
try {
// fetch the API key from environment
const API_KEY = await getAPIKey('wx');
//... rest of code
The code as it is works, but I don't understand why I need 3 await statements. Wouldn't I only need two? I need one for the fetch() in getAPIKey(). Then .json() returns a promise because it has to wait for the response body, so I'd need an await where I call the function in getLocalWeather(). But if I don't have two awaits in getAPIKey() it just returns [object Response]?
Essentially I'm wondering why the following is wrong:
export default async function getAPIKey(key) {
return (await fetch('http://localhost:8000/' + key)).json();
}
And in my weather object:
export default {
URI: 'https://api.openweathermap.org',
getLocalWeather: async function(city=null, countryCode=null) {
try {
// fetch the API key from environment
const API_KEY = await getAPIKey('wx');
//... rest of code
Am I miss-counting? Because I only see two Promises. I know async/await functions are promises under the hood, so getAPIKey() is a promise, but wouldn't that promise be the .json() Promise? And if so why isn't the await where I call the function sufficient?
I'm not sure what what I'm failing to understand.
You don't need any of those await statements inside of getAPIKey() and your function doesn't even need to be async. You can just do this:
export default function getAPIKey(key) {
return fetch('http://localhost:8000/' + key).json();
}
You just want to return the promise from fetch().json().
The code as it is works, but I don't understand why I need 3 await statements. Wouldn't I only need two?
Actually, you only need one when you do await getAPIKey(). The others inside of getAPIKey() are not needed at all.
When you do something like:
export default async function getAPIKey(key) {
return await fetch('http://localhost:8000/' + key).json();
}
You're just adding a superfluous await that has no benefit. The function returns a promise (all async functions return a promise) and that promise is resolved when the await is done which is exactly the same as just doing return fetch('http://localhost:8000/' + key).json(); in the first place. Adding the second await also adds no value.
Related
I am new to rxjs and not sure how to implement the follow logic. Any suggestion will be appreciated.
Background
I am going to implement the communication between host website and an iframe in it with postMessage. Since postMessage is one-way only, I would like to implement the logic to wait for 'response' by myself when a message is sent from host website to iframe.
I have a sync function called send(message) to invoke the postMessage to send message to iframe. Then I would like to have another function with the follow logic.
public async sendAndWait(message): Promise<responseObj> {
// 1. create an observable to wait to message event with timeout
// my first thought is as follow but I feel like it does not work
// fromEvent(window, 'message')
// .pipe(timeout(timeoutInMs))
// .subscribe(event => {
// console.info(event);
// });
// 2. run `send(message)` function
// 3. do not finish this function until timeout or receive event in the previous subscription.
}
When I use the function, I would like to have
let response = await sendAndWait(message);
Not sure if it is possible to implement? Thank you
You cannot stop code execution in JS (using Async-Await, a Promise object is returned behind the scenes. so that the code is never waiting)
Consider implementing it in the following way:
let response: responseObj;
function main(): void {
sendAndWait(MESSAGE_OBJECT).subscribe(x => response = x)
}
function sendAndWait(message): Observable<responseObj> {
send(message)
return fromEvent(window, 'message')
.pipe(
timeout(timeoutInMs),
first()
)
}
Or optionally returning Promise:
async function sendAndWait(message): Promise<void> {
send(message)
const response = await fromEvent(window, 'message')
.pipe(
timeout(timeoutInMs),
first(),
toPromise()
)
}
I'm new to JS and am currently immersing myself in asynchronous functions and promises. Having quite some experience in Python and R, this is totally new for me. I read many different websites, topics and solutions for returning a value in an asynchronous function - but I can't get it to work. Below I boiled it down to a simplification of the function I wrote, designed to get information from google about a location and return it. Simple as that. I followed the advice online and tried to rewrite the following advice from Benjamin Gruenbaum on How do I return the response from an asynchronous call?.
async function foo(){
var data = await fetch("/echo/json"); // notice the await
// code here only executes _after_ the request is done
return data.json(); // data is defined
}
Please see my own code below. It seems to me that it I'm doing the same thing, but it still logs as Promise {<pending>}... What am I doing wrong? data should be an array. Even if I only replace my googleapi url and use fetch() and .json(), it logs as Promise {<pending>}.
async function foo() {
var data = await axios.get("https://maps.googleapis.com/maps/api/geocode/json?address=Amsterdam&key=API_KEY");
return data;
}
console.log(foo())
try This way of calling async function
async function foo()
{
let response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?address=Amsterdam&key=API_KEY`);
let data = await response.json()
return data;
}
foo().then(data => console.log(data));
Can you try below code to know what is going on in the "data" you got .
This is not the solution of your problem but just you will know what you got in result
console.log(JSON.stringify(foo()));
When using next-redux-wrapper how do I start a long asynchronous task so that it only runs on the client? I don’t want to use await on the server side since it would delay the initial page load. I’d rather set a loading flag as the task starts and show a loading indicator until it completes.
Let’s say my async operation looks like this:
function async takesLong(store) {
store.dispatch({type: “LOADING”, payload: true});
const result = await longOperation();
store.dispatch({type: “SETDATA”}, payload: data);
return store.dispatch({type: “LOADING”, payload: false});
}
I can call this in my Next page’s getInitialProps function like this:
MyPage.getInitialProps = async ({ store, isServer }) => {
const loader = takesLong(store)
if (isServer) await loader; // <-- will delay client response
return {
someprop: "some value"
};
};
This works well if the page loads on the client side. The operation starts, and my page can display a loading-spinner until the operation completes. But when started on the server side I have a long delay before the page displays at all. I’ve tried a number of approaches but can’t find one that works properly:
Starting the process on the server and not using await renders the page without the results being written to the store, so it has only set “loading” to true in the store and it never gets the data.
Passing store in my props doesn’t work - it ends up being an empty object ({ }) in the client.
Trying to run it inside my component doesn’t seem to work for a few reasons:
a) I don’t have the store object accessible in the React Component (only in getInitialProps which won’t get called on the client).
b) Even if I use actions instead of store.dispatch in the Component, when can I call it safely? I can’t do it during render since it will change the Redux state, and componentWillReceiveProps won’t get called before the first client-side render
Is there a well defined pattern for deferring a long operation to the client-side when using Next?
Do your long async task on componentDidMount, it will run only on client side.
React in SSR not runs componentDidMount lifecycle hook.
Using bound actions during componentDidMount works. Thanks to #eenagy for the suggestion. Doing things in this order seems to do what is needed:
import { bindActionCreators } from "redux";
import { setLoading, setError, setData } from "../actions";
componentDidMount() {
if (!this.props.haveData && !this.props.loading && !this.props.error) {
this.props.setLoading(true);
loadSomeData() // <-- this takes a while to complete
.then( data => {
this.props.setData(data);
this.props.setLoading(false);
})
.catch( err => {
this.props.setError(err);
this.props.setLoading(false);
});
}
}
render() {
if (this.props.loading) return (<Loading/>);
return (/*regular page*/);
}
export const mapDispatchToProps = dispatch => {
return bindActionCreators({ setLoading, setError, setData }, dispatch);
};
export default connect(mapStateToProps, mapDispatchToProps)(Component);
This way if the initial data is not already loaded (say by another page) it will get kicked off when the
component mounts and run asynchronously until the operation completes and
calls the actions in redux to cause the page to re-render.
Currently I use promises in the store actions but want to convert it into async/await. This is an example of the store action with promises:
fetchActiveWorkspace (context, workspaceID) {
if (workspaceID) {
return this.$axios.get(`#api-v01/workspaces/workspace/${workspaceID}`)
.then(response => {
context.commit('setActiveWorkspace', response.data)
})
.catch(err => {
throw err
})
} else {
return Promise.resolve(true)
}
},
This fetchActiveWorkspace action is resolved in components because it returns promise. How can I convert this code snippet into a async/await structure and use it in components?
This is how I would try to translate it; take into account that as I have no access to the original code in full context, I cannot try it first-hand to make sure it works; but still, this is how you can use async/await with promises.
// 1. Mark the function as `async` (otherwise you cannot use `await` inside of it)
async fetchActiveWorkspace(context, workspaceID) {
if (workspaceID) {
// 2. Call the promise-returning function with `await` to wait for result before moving on.
// Capture the response in a varible (it used to go as argument for `then`)
let response = await this.$axios.get(`#api-v01/workspaces/workspace/${workspaceID}`);
context.commit('setActiveWorkspace', response.data);
}
// 3. I don't think this is necessary, as actions are not meant to return values and instead should be for asynchronous mutations.
else {
return true;
}
}
You can surround the function's body with try/catch in case you want to capture and handle exceptions. I didn't add it in order to keep things simple and because your promise-based code will just capture and re-throw the exception, without doing anything else.
promises are executed only if you call .then on them
or this is how I learned it.
Async functions are what I understand functions "turned" promises. Do I need to call .then on them each time I want to invoke an async function?
async function loadStory(){}
....
loadStory()
or
loadStory().then
Async functions are executed as per regular functions by invoking them as usual.
In order to make your code appear synchronous and utilise the benefits of async / await you'll need to prefix the call with await.
For example:
async function example () {
return new Promise(resolve => {
resolve('hello');
});
}
const myReturnedValue = await example();
Because of the await keyword, myReturnedValue will be the result of the resolved promise returned by the example function.