How to add await inside Promise? - javascript

Is it possible to add Await inside new promise ?
Originally, I don't need to put a await before making any request to the server. But one day, the server requires every request to have a token before sending out.
Let's take an example of the code
export const countries = (data: IData) => {
const countryRequests = getRequests(data)
const countryResponse = countryRequests?.reduce((countryResponse, request) => {
const countryResponses = new Promise((resolve, reject) => {
instance
.post(`/country`, request)
.then(data => {
resolve(data)
})
.catch(err => {
reject(err)
})
})
return [...countryResponse, countryResponses]
}, [])
return countryResponse
}
new code( putting async into the callback of promise):
export const countries = (data: IData) => {
const countryRequests = getRequests(data)
const countryResponse = countryRequests?.reduce((countryResponse, request) => {
const countryResponses = new Promise(async (resolve, reject) => { //add async here
await addAccessToken() // add header into token before sending the requests
instance
.post(`/country`, request)
.then(data => {
resolve(data)
})
.catch(err => {
reject(err)
})
})
return [...countryResponse, countryResponses]
}, [])
return countryResponse
}
addToken function:
export const addAccessToken = async () => {
const accessToken = await instance.get<IAccessToken>(
'/access_token'
)
const { access_token } = accessToken.data
instance.defaults.headers.common['Authorization'] = `Be ${access_token}`
}
But then I got a error below
Promise executor functions should not be async.(no-async-promise-executor)
How can I get rid of the error?
-------------- new changes---------
export const countries = async (data: IData) => {
const countryRequests = getRequests(data)
await addAccessToken()
const countryResponse = countryRequests?.reduce((countryResponse, request) => {
const countryResponses = instance
.post(`/country`, request) //------- May I ask, if it is successful call, then this will autmactically equvlanet to calling resolve (data) in my previosu code?
.catch(err => {
console.error(err)
})
return [...countryResponse, countryResponses]
}, [])
return countryResponse
}
added new prmosie.all part
const countryResponses = countries(data)
//set content for api 1
Promise.all([...countryResponses])
.then(values => {
const countryResponsesResult = values.map((value, _index) => {
return value.data.result ? value.data.result : []
})
//Set content for api 1
props.setProjection({
kind: 'success',
payload: {
data: countryResponsesResult,
},
})
})
.catch(_error => {
//Set content for api 1
props.setProjection({
kind: 'fail',
payload: {
error: new Error(_error.message),
},
})
})

As #deceze mentioned, instance already returns a promise for you so no need to return your own promise.
Just
export const countries = async (data: IData) => {
await addAccessToken()
const countryResponses = await instance.post(`/country`, request)
//your code//
If you don't want to use await and use promises and then instead you should have something like the below(promise chaining):
export const countries = (data: IData) => {
addAccessToken()
.then((data)=>{
const countryResponses = instance.post(`/country`,
request)
})
.then(//your code//)

Somehow I found this work.
export const countries = (data: IData)=> {
const countryRequests = getRequests(data)
const countryResponse = countryRequests?.reduce((countryResponse, request) => {
// return promise
const countryResponses = new Promise((resolve, reject) => {
addAccessToken().then(()=>{
instance
.post(`/country`, request)
.then(data => {
// change the returned propmise state into resolved
resolve(data)
})
.catch(err => {
reject(err)
})
})
})
//return the whole set of simlationCalls promise. When all promise is resolved, promise all will be notified and excute whatever it needs to execute
return [...countryResponse, countryResponses]
}, [])
return countryResponse
}

Related

How to throw an error with promise all when there are no data returned?

I have created a page that loads some data from api and I want to throw an error from the promise all if the api returns no data. How can this be done?
export const fruitsColor = async () : Promise => {
const response = await fetch(`....`);
if(!response.ok){
throw new Error('Error happened')
}
const data = await response.json();
return data;
};
export const fruitsType = async (): Promise => {
const response = await fetch(`....`);
if(!response.ok){
throw new Error('Error happened')
}
const data = await response.json();
return data;
};
export const getFruitsData = async (): Promise => {
return await Promise.all([
fruitsColor,
fruitsType,
])
.then(values => {
const results: FruitsStoreType = {
color: values[0],
type: values[1],
};
return results;
})
.catch(() => ({
color: [],
type: [],
}));
};
I think you have to pass Promise.all([fruitsColor(), fruitsType()]
Promise.all expects Promises and you are passing functions' references.
So, try to add the () to both the functions.
In this way the promise.all will catch the error
throw an error there to reach your goal
export const fruitsColor = async (): Promise<any> => {
const response = await fetch(`....`);
if (!response.ok) {
throw new Error('Error happened');
}
const data = await response.json();
return data;
};
export const fruitsType = async (): Promise<any> => {
const response = await fetch(`....`);
if (!response.ok) {
throw new Error('Error happened');
}
const data = await response.json();
return data;
};
export const getFruitsData = async (): Promise<any> => {
return await Promise.all([fruitsColor(), fruitsType()])
.then((values) => {
const results: FruitsStoreType = {
color: values[0],
type: values[1],
};
return results;
})
.catch(() => {
throw new Error('my own error');
});
};

Multiple API calls with Promise.all and dispatch an action

I want to call multiple API's and store each response data in an object then I want to dispatch this response object but I'm getting undefined.
Below is the code I tried. May I know where I'm doing wrong?
/* COMPONENT.JSX */
componentDidMount() {
callApis(this.props.products, this.props.profileId);
}
/* API.JS */
const getContactDetails = (http, profileId) =>
(http.get(`https://www.fakeurl.com/${profileId}/contact`));
const getProductDetails = (http, profileId) =>
(http.get(`https://www.fakeurl.com/${profileId}/product`));
const callApis = (products, profileId) => (dispatch) => {
const payload = new Map();
products.forEach((product) => {
const apis = [getContactDetails, getProductDetails];
apis.map(api => api(http, profileId));
Promise.all(apis)
.then((response) => {
const apiData = {
contactData: getParsedContactData(response[0]),
productData: getParsedProductData(response[1])
};
if (payload.get(product.token)) {
payload.get(companion.token).push(apiData);
} else {
payload.set(product.token, [apiData]);
}
})
.catch(err => {
throw ('An error occurred ', err);
});
});
dispatch({ type: FETCH_API_DATA, payload: payload });
}
I expect the dispatch will be called after all API's were resolved, get parsed, and map into the payload object then it should dispatch.
Array.map returns a new Array, which you are discarding
you're calling dispatch before any of the asynchronous code has run
A few minor changes are required
/* API.JS */
const getContactDetails = (http, profileId) => http.get(`https://www.fakeurl.com/${profileId}/contact`);
const getProductDetails = (http, profileId) => http.get(`https://www.fakeurl.com/${profileId}/product`);
const callApis = (products, profileId) => (dispatch) => {
const payload = new Map();
// *** 1
const outerPromises = products.map((product) => {
const apis = [getContactDetails, getProductDetails];
// *** 2
const promises = apis.map(api => api(http, profileId));
// *** 3
return Promise.all(promises)
.then((response) => {
const apiData = {
contactData: getParsedContactData(response[0]),
productData: getParsedProductData(response[1])
};
if (payload.get(product.token)) {
payload.get(companion.token).push(apiData);
} else {
payload.set(product.token, [apiData]);
}
})
.catch(err => {
throw ('An error occurred ', err);
});
}));
// *** 4
Promise.all(outerPromises)
.then(() => dispatch({
type: FETCH_API_DATA,
payload: payload
})
)
.catch(err => console.log(err));
}
rather than procucts.forEach, use products.map
capture the promises in apis.map to use in Promise.all
return Promise.all so the outer Promises can be waited for
Promise.all on the outer promises, to wait for everything to complete.
const callApis = (products, profileId) => async (dispatch) => { // use async function
const payload = new Map();
for (const product of products) {
const apis = [getContactDetails, getProductDetails];
apis.map(api => api(http, profileId));
await Promise.all(apis) // await all promise done
.then((response) => {
const apiData = {
contactData: getParsedContactData(response[0]),
productData: getParsedProductData(response[1])
};
if (payload.get(product.token)) {
payload.get(companion.token).push(apiData);
} else {
payload.set(product.token, [apiData]);
}
})
.catch(err => {
throw ('An error occurred ', err);
});
}
dispatch({ type: FETCH_API_DATA, payload: payload }); // dispatch will be executed when all promise done
}

Node-Fetch Mapping Error - Cannot read property 'map' of undefined"

Getting an error with the "map" part when I try and run it Cannot read property 'map' of undefined"
The customers const is declared above so not sure. Where is the undefined is coming from? Does the map need declaring?
const AWS = require('aws-sdk'),
ses = new AWS.SES(),
fetch = require('node-fetch');
exports.handler = async (event) => {
console.log(event.customer_id);
const customers = await getCustomers();
customers.map(async customer => await sendEmailToCustomer(customer));
const customersEmailsPromises = customers.map(async customer => await sendEmailToCustomer(customer));
}
async function getCustomers() {
try {
const resp = await fetch('https://3objects.netlify.com/3objects.json');
const json = await resp.json();
return json;
}
catch(e) {
throw e;
}
}
const sendEmailToCustomer = (customer) => new Promise((resolve, reject) => {
ses.sendEmail({
Destination:
{ ToAddresses: [customer.email] },
Message:
{
Body: { Text: { Data: `Your contact option is ${customer.customer_id}` } },
Subject: { Data: "Your Contact Preference" }
},
Source: "sales#example.com"
}, (error, result => {
if (error) return reject(error);
resolve(result);
console.log(result);
})
);
})
getCustomers doesn't return anything which means that customers is set to undefined.
Try this:
async function getCustomers() {
try {
const resp = await fetch('https://3objects.netlify.com/3objects.json');
const json = await resp.json();
return json;
}
catch(e) {
throw e;
}
}
You also have to return something from the function that you pass as a parameter to .map
customers.map(async customer => {
return await sendEmailToCustomer(customer);
});
or just:
customers.map(async customer => await sendEmailToCustomer(customer));
And since .map returns a new array (does not mutate the original array), you'll have to store the return value:
const customersEmailsPromises = customers.map(async customer => await sendEmailToCustomer(customer));

How to break chain in promise

I'm trying to limit the number of apis fetches in my project by saving them in a simple cache, key collection in mongodb. Is thera way to stop propagation of .then() inside Promise, without using async/await?
export const getData = (url: string) => {
return new Promise((resolve, reject) => {
findInCache(url)
.then((cached: string | null) => {
if (cached) {
resolve(cached);
}
})
.then(() => {
axios
.get(url)
.then(({data}) => {
setCache(url, data, TTL);
resolve(data);
})
.catch(e => reject(e));
});
});
};
Firstly, lets get rid of the Promise constructor anti-pattern - your function call inside the promise executor returns a promise, so, no need for anew Promise
Secondly, only run the second request if the result of the first is empty
export const getData = (url) => findInCache(url)
// here we return haveResult and avoid axios.get(url) altogether
.then((haveResult) => haveResult || axios.get(url)
// sometimes nested .then is useful like in this case
.then(({data}) => {
setCache(url, data, TTL);
return data;
})
);
you can just do this instead instead of chaining. if it is in cache then fetch from cache else get from url
export const getData = (url: string) => {
return new Promise((resolve, reject) => {
findInCache(url)
.then((cached: string | null) => {
if (cached) {
resolve(cached);
} else {
axios
.get(url)
.then(({data}) => {
setCache(url, data, TTL);
resolve(data);
})
.catch(e => reject(e));
}
})
});
};
When you return something result in then, this result is come into next then function. So, you can control what you would do in next then based on input parameter inCache. So you can do something like:
export const getData = (url: string) => {
return new Promise((resolve, reject) => {
findInCache(url)
.then((cached: string | null) => {
if (cached) {
resolve(cached);
return true;
}
return false;
})
.then((inCache) => {
if (!inCache) {
axios
.get(url)
.then(({data}) => {
setCache(url, data, TTL);
resolve(data);
})
.catch(e => reject(e));
}
});
});
};

Redux Actions must be plain objects - error with store setup or action config?

Morning all... getting Actions must be plain objects. Use custom middleware for async actions. with the following action:
export const addHousehold = user => dispatch =>
new Promise(async (resolve, reject) => {
const hasHousehold = await getUsersHouseholdByUserId(user.uid);
if (hasHousehold) {
return reject(
new Error(
'You have already generated a household. Please complete the setup steps.',
),
);
}
return resolve(dispatch(createHousehold(user)));
});
The Promise should be implicitly returned from this function call, shouldn't it? The action is called on click from a component, the call is here:
addHousehold() {
this.props.addHousehold(this.props.user).then(
() => {
this.props
.addOnboardingStages(this.props.user.uid)
.then(res => {}, err => {});
},
err => this.setState({ errors: { generateHouseholdError: err.message } }),
);
}
This is my store setup, using Redux thunk as middleware.
const store = createStore(
rootReducer,
INITIAL_STATE,
applyMiddleware(thunk),
);
UPDATE:
The createHousehold function looks as follows:
const createHousehold = user => {
const { uid } = user;
const householdId = uniqid.time();
return Promise.all([
setHousehold(uid, householdId),
setUser(uid, householdId),
]);
};
const setHousehold = (uid, householdId) => dispatch =>
new Promise((resolve, reject) => {
console.log(uid)
db
.collection('households')
.doc(householdId)
.set(
{
users: [uid],
},
{ merge: true },
)
.then(() => {
resolve();
})
.catch(() => reject());
});
const setUser = (uid, householdId) => dispatch =>
new Promise((resolve, reject) => {
db
.collection('users')
.doc(uid)
.update({
household: householdId,
})
.then(
() => resolve(dispatch(receiveHousehold(householdId))),
err => console.error(err),
);
});
UPDATE 2
Looks like the issue was dispatch wasn't passed in to createHousehold:
const createHousehold = user => dispatch => {
const { uid } = user;
const householdId = uniqid.time();
return Promise.all([
setHousehold(uid, householdId),
setUser(uid, householdId)(dispatch) // here
]);
};
Thank you :)

Categories

Resources