Wait till await/async returns in Jquery - javascript

I am new to await/async in Jquery. When I am trying to use it in my js file, It is getting executed, but not as expected.
async updateProduct (product) {
product.travelers.forEach((traveler, travelerOrder) => this.updateProductwithPassengerName(traveler) )
return product
}
async updateProductwithPassengerName (traveler) {
const travelerId = traveler.profile_id;
var body = await this.userServiceClient.request('/v2/users/' + travelerId + '/info','GET')
traveler.first_name = body.first_name
traveler.last_name = body.last_name
return traveler
}
async request (path, method, body) {
const options = {
method: method,
body: body,
headers: this.headers(),
credentials: 'same-origin'
}
const response = await fetch(this.relativeUrl(path), options)
if (!response.ok) {
throw new HttpError('Technical error occured', response.status)
}
return response.json().catch(function () {
throw new HttpError('No results found', response.status)
})
}
Those are the 3 functions. What is happening now is that
traveler.first_name = body.first_name
traveler.last_name = body.last_name
these are not setting in synchronous manner( after
var body = await this.userServiceClient.request('/v2/users/' +
travelerId + '/info','GET')
. These are executing after a quiet a long time.
What I am doing here is that, for each traveler I am setting first_name and last_name. And updating the product object. Since this setting of values is happening after a long time, product object is getting updated later, by the time UI page renders. I want this setting values to happening before doing anything else in jquery.
Looking for help

The problem is that forEach will not await the asynchronous result and so your first function returns a promise that immediately resolves, without waiting for the requests to finish.
Here is how to correct:
async updateProduct (product) {
await Promise.all(product.travelers.map((traveler, travelerOrder) => this.updateProductwithPassengerName(traveler) ));
return product
}
Or, if the backbone cannot deal with all these concurrent requests, then wait for each of them to complete before issuing the next:
async updateProduct (product) {
for (let traveler of product.travelers) {
await this.updateProductwithPassengerName(traveler);
}
return product
}

when using asyn/await in JQuery or angularjs or nodejs, use promise. Promise will complete the process and then return either success or fail. Then only execute next steps.Go through below links and you will get idea.
Modern Asynchronous JavaScript with Async and Await
async & await in Javascript

Related

How to make Javascript wait for a Promise before sending a request?

I know there are many questions and answers based on Promise, but what I want to do is to retrieve some data with axios (to a microservice) and then to use this data in order to send another request (to a different microservice).
Somehow, I figured out how to set my request:
screenshot from console with the request right before axios call
The problem is that in backend I have only first two clauses. I think this is because I have used async/await in order to successfully avoid Promise and get the actual result/class. What I meant is that, maybe the request is sent before the promise is fulfilled, but how do I correctly get the request in console?
I am newbie into Javascript, so any helping hand is welcome.
EDIT:
Here is my code:
getServicesList = async (instanceIds) => {
return await FlowsInventoryAPI.searchServices(instanceIds, this.props.salesline, this.props.environment, this.props.sources, this.props.targets)
.then((response) => {
return response;
})
.catch((error) => {
Toastr.error(ResponseErrorProvider.getError(error));
if (ResponseErrorProvider.isUnauthorized(error)) {
Toastr.error(error.response.data.message);
this.props.onLogout();
}
});
}
The above one is the first call I've talked about.
buildSearchObject = (size, page, status) => {
let interval = TimestampUtils.getInterval(this.props.logsTimeInterval);
let from = interval.from * 1000;
let to = interval.to * 1000;
return {
fromMillis: from,
toMillis: to,
size: size,
page: page,
salesline: this.props.salesline,
environment: this.props.environment,
routes: this.props.routes,
sources: this.props.sources,
targets: this.props.targets,
customFilters: [...this.props.filters.values(), ...this.getEnabledAdvancedFilters().values()],
status: status === LogStatus.ALL ? "" : status,
sortFieldName: this.props.sortColumn,
order: this.props.sortOrder,
searchFilter: this.props.searchFilter,
searchFilterOperator: this.props.searchFilterOperator,
applications: this.props.applications,
openedStores: this.props.openedStores,
servicesPromise: this.state.servicesList // here is the promise
}
};
searchLogs = (size, page, status, callback) => {
loadingService.showLoadingModal("loadingLogsPage", this.props.location.pathname);
let searchObject = this.buildSearchObject(size, page, status);
ElasticSearchApi.search(searchObject, this.props.token)
.then(response => {
callback(response);
})
.catch((error) => {
loadingService.hideLoadingModal("loadingLogsPage", this.props.location.pathname);
Toastr.error(ResponseErrorProvider.getError(error));
if (ResponseErrorProvider.isUnauthorized(error)) {
Toastr.error(error.response.data.message);
this.props.onLogout();
}
});
};
I have the second call in last paragraph which calls the buildSearchObject method which contains our promise. As I told you I figured out how to send it as value, but I think that because of "asynchronicity" maybe my promise is not ready yet in the moment when second call is called, this is why my code has the promise in state.
EDIT 2:
constructor(props) {
super(props);
this.ongoingRequestId = undefined;
this.ongoingRequests = new Map();
this.state = {
servicesList: this.getServicesList(this.getInstanceIds())
}
}
Here is my constructor, where I create my this.state.servicesList.
Some advice:
Do not mix traditional promises syntax with async / await. It will make your code hard to understand, even for yourself. Do not mix either callback approach with promises. Choose one approach and stick to it.
If you are having a hard time with promises, force yourself to use async / await everywhere. async / await is easier to understand in my opinion, because it doesn't break your code flow.
For instance, transform this:
FlowsInventoryAPI.searchServices(/* params */)
.then((response) => /* response code */)
.catch((error) => /* error code */)
to:
try {
const response = await FlowsInventoryAPI.searchServices(/* params */);
/* response code */
} catch (error) {
/* error code */
}
Do not make your constructors asynchronous like you do where you call this.getServicesList, because you cannot wait for an asynchronous operation (like getServicesList) inside a constructor. Use instead a static async method.
For instance, transform this:
class SomeObject extends Something {
constructor(props) {
super(props);
this.ongoingRequestId = undefined;
this.ongoingRequests = new Map();
this.state = {
servicesList: this.getServicesList(this.getInstanceIds())
}
}
}
to:
class SomeObject extends Something {
constructor(props) {
super(props);
this.ongoingRequestId = undefined;
this.ongoingRequests = new Map();
this.state = { servicesList: null };
}
async init() {
this.state.servicesList = await this.getServicesList(this.getInstanceIds());
}
static async create(props) {
const someObject = new SomeObject(props);
await someObject.init();
return someObject;
}
}
Instead of calling const object = new SomeObject(props);, do const object = await SomeObject.create(props);
You will need to use await keyword to wait for a promise response before continuing.
// 1. Wait for create or update the customer before continuing
const customerId = await updateOrCreateCustomer(customerData);
// 2. Register sale, with customer created in previous section
const customerSale = sale(paymentMethod, customerId);
Read more about the await keyword

How do I pass a parameter to API request 2, which I receive from API response 1 in react

My 2 API calls happen to be at the same time, where the response of API1 is to be sent as a request parameter to API2. But, the value goes as undefined because it isn't fetched till that time. Is there any way this can be solved in react.
There are multiple ways to solve this problem, I will explain one of the latest as well most sought after ways of solving the problem.
I am sure you would have heard of async/await in JavaScript, if you haven't I would suggest you to go through an MDN document around the topic.
There are 2 keywords here, async && await, let's see each of them one by one.
Async
Adding async before any function means one simple thing, instead of returning normal values, now the function will return a Promise
For example,
async function fetchData() {
return ('some data from fetch call')
}
If you run the above function in your console simply by fetchData(). You'd see that instead of returning the string value, this function interestingly returns a Promise.
So in a nutshell async ensures that the function returns a promise, and wraps non-promises in it.
Await
I am sure by now, you would have guessed why we use keyword await in addition to async, simply because the keyword await makes JavaScript wait until that promise (returned by the async function) settles and returns its result.
Now coming on to how could you use this to solve your issue, follow the below code snippet.
async function getUserData(){
//make first request
let response = await fetch('/api/user.json');
let user = await response.json();
//using data from first request make second request/call
let gitResponse = await fetch(`https://api.github.com/users/${user.name}`)
let githubUser = await gitResponse.json()
// show the avatar
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
// wait 3 seconds
await new Promise((resolve, reject) => setTimeout(resolve, 3000));
img.remove();
return githubUser;
}
As you can see the above code is quite easy to read and understand. Also refer to THIS document for more information on async/await keyword in JavaScript.
Asyn/await solves your problem:
const requests = async () =>{
const response1 = await fetch("api1")
const result1 = await response1.json()
// Now you have result from api1 you might use it for body of api2 for exmaple
const response2 = await fetch("api2", {method: "POST", body: result1})
const result2 = await response1.json()
}
If you're using react hooks, you can use a Promise to chain your API calls in useEffect
useEffect(() => {
fetchAPI1().then(fetchAPI2)
}, [])
relevant Dan Abramov
fetch(api1_url).then(response => {
fetch(api2_url, {
method: "POST",
body: response
})
.then(response2 => {
console.log(response2)
})
})
})
.catch(function (error) {
console.log(error)
});
or if using axios
axios.post(api1_url, {
paramName: 'paramValue'
})
.then(response1 => {
axios.post(api12_url, {
paramName: response1.value
})
.then(response2 => {
console.log(response2)
})
})
.catch(function (error) {
console.log(error);
});

Why is function returning undefined when calling fetch, axios, ajax, or a promise [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
When attempting to use asynchronous calls, the response is undefined rather than returning back the expected response data.
You can see how to run into this issue with any of the following examples:
Fetch
const response = getDataWithFetch();
console.log(response);
function getDataWithFetch() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
fetch(url).then(response => {
return response.json();
});
}
Axios
const response = getDataWithAxios();
console.log(response);
function getDataWithAxios() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
axios.get(url).then(response => {
return response.data;
});
}
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
AJAX / jQuery $.get
const response = getDataWithAjax();
console.log(response);
function getDataWithAjax() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
$.get(url, (response) => {
return response;
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Promise
const response = getDataWithPromise();
console.log(response);
function getDataWithPromise() {
new Promise(resolve => {
const response = 'I am a response';
return resolve(response);
});
}
Overview
Asynchronous calls, such as HTTP get requests via fetch, axios, AJAX or other asynchronous calls via Promises all behave similarly in that what is returned by the time that the function reaches its last line is not actually the result that you want.
Explanation of Issue
This is because asynchronous calls do not wait for the call to finish executing before executing the next line:
Note that in all the examples below, I will be using fetch but the concept could easily be extended to the other call types.
getDataWithFetch();
function getDataWithFetch() {
console.log('start of outer function');
const url = 'https://jsonplaceholder.typicode.com/todos/1';
fetch(url).then(response => {
console.log('start of async function');
response.json();
console.log('end of async function');
});
console.log('end of outer function');
}
As you can see via Run code snippet, the outer function starts and ends before the async function starts. What is undefined being returned from getDataWithFetch() because it is hitting the end of the function, and has no return statement. If we change the code to add a return statement, we can see what part of the function call is actually being returned:
const response = getDataWithFetch();
console.log(response);
function getDataWithFetch() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
fetch(url).then(response => {
return response.json();
});
return 'hello world';
}
Now the function isn't returning undefined anymore, but it's not returning the fetch request response; instead, it is returning 'hello world'. This is because the fetch request is not blocking the rest of the getDataWithFetch function from executing. To understand why, we need to understand what fetch is doing behind the scenes.
Promises
Resolve
To do so, we need to understand Javascript Promises. There's a great beginner-friendly read on it here: https://javascript.info/promise-basics, but in a nutshell:
A Promise is an object that, when it finishes the asynchronous work, it will call the "resolve" function. Where "resolve" returns the expected data via resolve(dataToReturn).
So behind the scenes, fetch returns a Promise that will call resolve when the fetched data is retrieved, like so:
// just an example of what fetch somewhat looks like behind the scenes
function fetch(url) {
return new Promise(resolve => {
// waiting for response to come back from server
// maybe doing a bit of additional work
resolve(responseDataFromServer);
});
}
With that understanding, we now know that we need to return the call to fetch to actually return the Promise, like so:
const response = getDataWithFetch();
console.log(response);
function getDataWithFetch() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
return fetch(url).then(response => response.json());
}
The console.log is no longer returning undefined, but it's not returning the right response either. That's because getDataWithFetch is now returning the Promise object from fetch. It's time to get the response from resolve.
.then
To get the data passed to the resolve function, we need to use the then method on the returned Promise.
A Promise provides the method then to retrieve the value passed to resolve as such: myReturnedPromise.then(dataToReturn => { /* do something with the data */ });. then will only execute once the dataToReturn is ready to be handled.
Final Solution
So by using the then method, we can get the actual response printed to console:
getDataWithFetch().then(response => {
console.log(response);
});
function getDataWithFetch() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
return fetch(url).then(response => response.json());
}
And there you have it!
Overly Verbose Solution
If there was too much magic in what made that solution work, you can also see the breakdown of it by explicitly returning a Promise to be fulfilled:
getDataWithFetch().then(response => {
console.log(response);
});
function getDataWithFetch() {
return new Promise(resolve => {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
return fetch(url).then(response => {
resolve(response.json()));
// Note that the `resolve` does not have to be part of the `return`
}
});
}
Async and Await Solution
Recent improvements to Javascript have provided us with a more succinct way of handling Promises to make them appear synchronous instead of asynchronous. Note that the following code is equivalent to the above solutions, but much easier to read:
async function getDataWithFetch() {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
const response = await fetch(url);
return response.json();
}
getDataWithFetch().then(response => {
console.log(response);
});
async and await are clean and powerful alternatives to explicitly dealing with Promises, especially when Promise Chaining is involved. I recommend reading more about them here: https://javascript.info/async-await.

JavaScript Promise inside async/await function resolve final array of responses

I'm quite a newbie in JavaScript and in Promises.
I'm trying to build an array of objects that I get from an API.
To do so, I've build two functions in a file MyFile.js.
The first one returns a promise when an axios promise is resolved. It's
function get_items (url) {
return new Promise((resolve, reject) => {
let options = {
baseURL: url,
method: 'get'
}
axios(options)
.then(response => {
resolve(response.data)
})
.catch(error => {
reject(error.stack)
})
})
}
The second one looks like this:
let output = []
let next_url = 'https://some_url.com/api/data'
async function get_data () {
try {
let promise = new Promise((resolve, reject) => {
if (next_url) {
get_items(next_url)
.then(response => {
output.push(...response.results)
if (response.next) {
next_url = response.next
console.log('NEXT_URL HERE', next_url)
get_data()
} else {
console.log('else')
next_url = false
get_data()
}
})
.catch(error => {
reject(error.stack)
})
} else {
console.log('before resolve')
resolve(output)
}
})
return await promise
} catch(e) {
console.log(e)
}
}
It's where I'm grinding my teeth.
What I think I understand of this function, is that:
it's returning the value of a promise (that's what I understand return await promise is doing)
it's a recursive function. So, if there is a next_url, the function continues on. But if there is not, it gets called one last time to go into the else part where it resolves the array output which contains the results (values not state) of all the promises. At least, when I execute it, and check for my sanity checks with the console.log I wrote, it works.
So, output is filled with data and that's great.
But, when I call this function from another file MyOtherFile.js, like this:
final_output = []
MyFile.get_data()
.then(result => {
console.log('getting data')
final_output.push(...result)
})
it never gets into the then part. And when I console.log MyFile.get_data(), it's a pending promise.
So, what I would like to do, is be able to make get_data() wait for all the promises result (without using Promise.all(), to have calls in serie, not in parallel, that would be great for performances, I guess?) and then be able to retrieve that response in the then part when calling this function from anywhere else.
Keep in mind that I'm really a newbie in promises and JavaScript in general (I'm more of a Python guy).
Let me know if my question isn't clear enough.
I've been scratching my head for two days now and it feels like I'm running in circle.
Thanks for being an awesome community!
This is a bit untested
const api_url = 'https://some_url.com/api/data';
get_data(api_url).then((results) => {
console.log(results);
}).catch((error) => {
// console.error(error);
});
function get_items (url) {
const options = {
baseURL: url,
method: 'get'
};
return axios(options).then((response) => response.data);
}
async function get_data(next_url) {
const output = [];
while (next_url) {
const { results, next } = await get_items(next_url);
output.push(...results);
next_url = next;
}
return output;
}
Basically it makes things a bit neater. I suggest to look at more examples with Promises and the advantage and when to ease await/async. One thing to keep in mind, if you return a Promise, it will follow the entire then chain, and it will always return a Promise with a value of the last then.. if that makes sense :)
There are a few problems. One is that you never resolve the initial Promise unless the else block is entered. Another is that you should return the recursive get_data call every time, so that it can be properly chained with the initial Promise. You may also consider avoiding the explicit promise construction antipattern - get_items already returns a Promise, so there's no need to construct another one (same for the inside of get_items, axios calls return Promises too).
You might consider a plain while loop, reassigning the next_url string until it's falsey:
function get_items (baseURL) {
const options = {
baseURL: url,
method: 'get'
}
// return the axios call, handle errors in the consumer instead:
return axios(options)
.then(res => res.data)
}
async function get_data() {
const output = []
let next_url = 'https://some_url.com/api/data'
try {
while (next_url) {
const response = await get_items(next_url);
output.push(...response.results)
next_url = response.next;
}
} catch (e) {
// handle errors *here*, perhaps
console.log(e)
}
return output;
}
Note that .catch will result in a Promise being converted from a rejected Promise to a resolved one - you don't want to .catch everywhere, because that will make it difficult for the caller to detect errors.
Another way of doing it is to not use async at all and just recursively return a promise:
const getItems = (url) =>
axios({
baseURL: url,
method: 'get',
}).then((response) => response.data);
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl).then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl)
.catch(e=>Promise.reject(e.stack));//reject with error stack
};
As CertainPerformance noted; you don't need to catch at every level, if you want getData to reject with error.stack you only need to catch it once.
However; if you had 100 next urls and 99 of them were fine but only the last one failed would you like to reject in a way that keeps the results so far so you can try again?
If you do then the code could look something like this:
const getData = (initialUrl) => {
const recur = (result, nextUrl) =>
!nextUrl
? Promise.resolve(result)
: getItems(nextUrl)
.catch(e=>Promise.reject([e,result]))//reject with error and result so far
.then((data) =>
recur(result.concat([data.results]), data.next),
);
return recur([],initialUrl);//do not catch here, just let it reject with error and result
};

How to process fetch() response and still work asynchronously

I have a JS program that does a whole lot of fetch() calls to a specific API. I want to abstract all the fetch() calls into a single class called "apiService" so my code will be more readable. I want the apiService to apply some intelligence and then return responses to the caller, in the following ways:
- apiService should check the response to see if there are errors present, which it must always process in the same way.
- fetch() will sometimes receive a "res" that is raw data and should be used as is, and sometimes it'll received json that needs a .then(res => res.json().then(res applied so it can return an object.
So I can't just do a "return fetch(..." from apiService, because apiService needs to process one or more .then() blocks with the response. But I also need to return something that causes the calling code to work asynchronously and not block and wait.
Anyone know how I could structure the apiService function to process the html responses but also return asynchronously i.e. the calling function would receive the result object after the error checking etc.
So I can't just do a "return fetch(..." from apiService, because apiService needs to process one or more .then() blocks with the response. But I also need to return something that causes the calling code to work asynchronously and not block and wait.
This gives me the feeling that you might be misunderstanding promises a little bit. Take this example:
const doAsyncWork = () => fetch('somewhere').then(() => console.log('fetch is complete'))
// use the above function
doAsyncWork().then(() => console.log('used the fetching function'))
The output of the above code will be
fetch is complete
used the fetching function
As you can see, by chaining then after the fetch call, you are actually returning the result of then, and not fetch. Another way to think of it is, what are you actually returning if you called
const result = a().b().c() // we are really returning the result of `c()` here.
With the above in mind you can most definitely do something like:
const apiCall = loc => fetch(loc).then(res => {
// do things with your response
return res
})
apiCall('someEndpoint').then(finalRes => {
console.log('this is called after fetch completed and response processed')
})
There is a nice article about it here called "Synchronous" fetch with async/await that will break it down for you in pieces.
In Short:
You can use await when using fetch():
const response = await fetch('https://api.com/values/1');
const json = await response.json();
console.log(json);
First we wait for the request to finish, Then we can wait for it to complete (or fail) and then pass the result to the json variable.
The complete example would be to use async, because `await won't work without it:
const request = async () => {
const response = await fetch('https://api.com/values/1');
const json = await response.json();
console.log(json);
}
request();
I thinks you can fulfill your requirement using Promise.all()
Here is an example for you.
var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then(function(values) {
console.log(values);
});
For more info you can refer:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
You can use a library called axios instead of worrying about promises and data formats yourself.
However, if you still want to do it, use the following way.
you can use a method to create promises like this.
makeRequest(url, requestData) {
const response = await fetch(url, requestData)
.then(response => { console.info('network request successful to', url); return response.json() })
.then(json => {
console.info('response received for request', url, requestData, json)
return json;
})
.catch(e => {
console.error('error at request', url, requestData, e);
return e
});
return response;
}
and use the promises like this
makeRequest('someurl', {
method: 'GET'
}).then(response=>{/*Your logic*/}).catch(error=>{/*Your logic*/});

Categories