I have this array of promises :
return function (dispatch, getState) {
Promise.all([
dispatch(getThis()),
dispatch(getThat()),
dispatch(getThese()),
dispatch(getThose()))]
).then(() => {
let state = getState();
dispatch({
//dispatch
});
}).catch(function (err) {
console.log(err);
});
}
But in fact I want getThis() to be finished before calling any other dispatch because they are all dependant on the return of getThis().
How to handle this?
Thanks a lot!
EDIT my function looks like this:
export function getThis() {
return function (dispatch, getState) {
return new Promise((resolve, reject) => {
axios.get('api/this/' + lienBuild).then((response) => {
resolve(dispatch({type: "FETCH_THIS", data: response.data}));
});
})
};
}
See FrankerZ's comment, apparently this dispatch function is a standard Redux thing (I don't use Redux) and it doesn't return a promise, so it doesn't make sense to use Promise.all on it.
But answering the promises aspect of "how would I wait for getThis first": Just move it to the beginning of the chain:
return function (dispatch, getState) {
dispatch(getThis()) // First this, then...
.then(result => Promise.all([ // ...these
dispatch(getThat(result)), // (I've passed the result to all three, but...)
dispatch(getThese(result)),
dispatch(getThose(result))
]))
.then(() => {
let state = getState();
dispatch({
//dispatch
});
}).catch(function (err) {
console.log(err);
});
}
or without the dispatch, since apparently that's wrong:
return function (dispatch, getState) {
getThis() // First this, then...
.then(result => Promise.all([ // ...these
getThat(result), // (I've passed the result to all three, but...)
getThese(result),
getThose(result)
]))
.then(() => {
let state = getState();
dispatch({
//dispatch
});
}).catch(function (err) {
console.log(err);
});
}
Side note about getThis: See this question and its answers. No need for new Promise when you already have a promise. I don't know whether that getThis code is correct in terms of Redux, but here's the same code not using new Promise unnecessarily:
export function getThis() {
return function(dispatch, getState) {
return axios.get('api/this/' + lienBuild).then(response => dispatch({type: "FETCH_THIS", data: response.data}));
};
}
or with destructuring and shorthand properties:
export function getThis() {
return function(dispatch, getState) {
return axios.get('api/this/' + lienBuild).then(({data}) => dispatch({type: "FETCH_THIS", data}));
};
}
or if you can use async/await:
export function getThis() {
return async function(dispatch, getState) {
const {data} = await axios.get('api/this/' + lienBuild);
return dispatch({type: "FETCH_THIS", data});
};
}
Related
I have a websocket interface which I implemented so that I can use to send requests.
The problem is that the response is asynchronous and it initially returns the empty array because retObj is not updated from the callback function that I sent in. How can I make this function so that it will return the populated array when it has been updated.
This is how my Service looks like:
import * as interface from '../webSocket'
const carService = () => {
return {
getCars: () => {
interface.sendRequest(function (returnObject) {
//
}).then(d => d)
}
}
}
export default carService()
And this is how my action looks like:
import { GET_CARS } from '../constants'
import carService from '../carService'
export const getCars = () => async (dispatch) => {
try {
const cars = await carService.getCars()
console.log("At cars actions: ", cars) // logs: Array []
dispatch(getCarsSuccess(cars))
} catch (err) {
console.log('Error: ', err)
}
}
const getCarsSuccess = (cars) => ({
type: GET_CARS,
payload: cars
})
You simply have to wrap your callback into promise, since it was not a promise to begin with, which is why you cannot use then or await
import * as interface from '../webSocket'
const carService = () => {
return {
getCars: () => {
return new Promise(resolve => interface.sendRequest(function (returnObject) {
resolve(returnObject.msg)
}));
}
}
}
export default carService()
The problem is, you cant await a function unless it returns a Promise. So, as you can guess, the problem lies in carService.getCars's definition. Try this:
getCars: () => {
return new Promise((resolve, reject) => {
interface.sendRequest(function(returnObject) {
// if theres an error, reject(error)
resolve(returnObject);
})
})
}
Or, if sendRequest os am async function, simply return the return value of sendRequest:
getCars: () => {
return interface.sendRequest()
}
I have a situation when I need 2 Redux Actions to be run consecutively.
The context is a user clicks on a Preview button, and I want to display a loader until the puzzle is done generating.
function mapDispatchToProps(dispatch) {
return {
onPreview: () => {
dispatch(generatePreview());
},
};
}
In order to do it, I use the middleware redux-thunk and the action I want to be executed first returns a Promise.resolve() and my second action is in the then():
export function generatingPreview() {
return dispatch => {
dispatch({
type: GENERATING_PREVIEW,
});
return Promise.resolve();
};
}
export function generatePreview() {
return (dispatch, getState) => {
dispatch(generatingPreview()).then(() => {
const state = getState();
const conf = state.getIn(['login', 'conf']).toJS();
const wordList = state.getIn(['login', 'wordList']);
try {
const newPuzzle = Wordfind.newPuzzleLax(wordList, conf);
dispatch(generatePreviewSuccess(newPuzzle));
} catch (err) {
dispatch(generatePreviewError(err.message));
}
});
};
}
export function generatePreviewError(error) {
return {
type: GENERATE_PREVIEW_ERROR,
error,
};
}
export function generatePreviewSuccess(payload) {
return {
type: GENERATE_PREVIEW_SUCCESS,
payload,
};
}
Unfortunately, the loader never appears. I console.logged the state setting the loading to true when my component renders, and it changes! I can see the log but not the loader, the component doesn't really re-render until the actions generatePreviewSuccess() or generatePreviewError() are dispatched. And it's not an issue from the loader, if I replace the newPuzzleLax function by a loop in order to make enough time to see it, I can see it!
My theory is this function Wordfind.newPuzzleLax(wordList, conf) that I use to generate the puzzle is blocking the queue of actions because on the Chrome Redux Tools I an see the first action appearing at the same time that the second one:
Link to the function.
If I add a 1-microsecond delay between the dispatch of the two actions, the loader appears... but I would really like to understand what is happening. Thank you in advance. If it's any help, I use the react-boilerplate
I also tried to transform the function generating the puzzle as an async one by doing this:
const wordFindAsync = async (wordList, conf) =>
Wordfind.newPuzzleLax(wordList, conf);
export function generatePreview() {
return (dispatch, getState) => {
dispatch(generatingPreview())
.then(() => {
const state = getState();
const conf = state.getIn(['login', 'conf']).toJS();
const wordList = state.getIn(['login', 'wordList']);
wordFindAsync(wordList, conf);
})
.then(res => dispatch(generatePreviewSuccess(res)))
.catch(err => {
dispatch(generatePreviewError(err.message));
});
};
}
In your second version you're not returning the Promise from wordFindAsync(wordList, conf) back into your original Promise chain, and so its not being resolved/waited on by then next then.
export function generatePreview() {
return (dispatch, getState) => {
dispatch(generatingPreview())
.then(() => {
const state = getState();
const conf = state.getIn(['login', 'conf']).toJS();
const wordList = state.getIn(['login', 'wordList']);
return wordFindAsync(wordList, conf); // 🌟 return your promise here
})
.then(res => dispatch(generatePreviewSuccess(res)))
.catch(err => {
dispatch(generatePreviewError(err.message));
});
};
}
Here's a simple example demoing the behavior I'm refering to.
This one will only wait 1 second until logging "done":
const waitOneSec = () =>
new Promise(resolve => {
console.log("waiting 1 secoond");
setTimeout(resolve, 1000);
});
waitOneSec()
.then(() => {
waitOneSec(); // Promise not returned
})
.then(() => console.log("done"));
Whereas this one will wait full 2 seconds until logging "done":
const waitOneSec = () =>
new Promise(resolve => {
console.log("waiting 1 secoond");
setTimeout(resolve, 1000);
});
waitOneSec()
.then(() => {
return waitOneSec(); // 🌟 Promise returned
})
.then(() => console.log("done"));
Hope that helps.
My nodejs api function:
exports.userSignup = (req, res) => {
const home = {
address: req.body.name,
phoneno: req.body.code,
};
Home.create(home)
.then((data) => {
createUser()
// i want to complete the above createUser() function fully then only i need to move to this below then function
.then(() => {
const loginDetails = {
username: 'stackoverflow',
};
User.create(loginDetails)
.then((data) => {
return res.status(200).send(data);
}).catch((err) => {
console.log('error while create schema:', err);
});
});
})
.catch((err) => {
console.log('err:', err);
});
};
My createUser function code:
const createUser = () => {
Home.findAll({
raw: true,
}).then((data) => {
data.forEach((client) => {
postgresDB.createSchema(client.code).then(() => {
Object.keys(postgresDB.models).forEach((currentItem) => {
postgresDB.models[currentItem].schema(client.code).sync();
});
console.log('Postgres schema created');
}).catch((err) => {
console.log(err);
});
});
}).catch((err) => {
console.log('Warning:', err.message);
});
};
createUser();
But this one is working as asynchronous,
How to make this using promise resolve reject or callback?
See my code, i made a comment which needs to work first,
I tried with async await but not working!
In order for promise chaining to work, you have to return a promise for any asynchronous functions or else it won't wait. You're also making another asynchronous call for each client you iterate through. In order to deal with multiple promises at once, you need to push each promise into an array and pass it to Promise.all. Here's a snippet that should work.
const createUser = () => {
return Home.findAll({
raw: true,
}).then((data) => {
const promises = [];
data.forEach((client) => {
promises.push(
postgresDB.createSchema(client.code).then(() => {
Object.keys(postgresDB.models).forEach((currentItem) => {
postgresDB.models[currentItem].schema(client.code).sync();
console.log('Postgres schema created');
})
})
);
});
return Promise.all(promises);
}).catch((err) => {
console.log('Warning:', err.message);
});
};
The problem lies in the synchronous data.forEach() call inside the createUser function which doesn't wait for the async createSchema calls to finish before returning.
You can fix this by using an async implementation of forEach function or by using Promise.all()
Here's a piece of code that might work for you:
const createUser = () => {
return Home.findAll({
raw: true
}).then((data) => Promise.all(
data.map((client) => postgresDB.createSchema(client.code).then(() =>
Promise.all(Object.keys(postgresDB.models).map((currentItem) =>
postgresDB.models[currentItem].schema(client.code).sync()
))
))
))
.catch((err) => {
console.log('Warning:', err.message);
});
};
If async/await doesn't quite work for you, just use promise chaining. It's super painful to see people just write callback based code with promises and forget that you can chain promises by returning a promise from the onFulfilled() parameter to then():
const createUser = () => {
return Home.findAll({ raw: true }).
then(data => {
return Promise.all(data.map(client => postgresDB.createSchema(client.code)));
}).
then(() => {
const keys = Object.keys(postgresDBModels);
return Promise.all(keys.map(currentItem => {
return postgresDB.models[currentItem].schema(client.code).sync();
}));
}).
then(() => console.log('Postgres schema created')).
catch((err) => {
console.log(err);
});
};
createUser();
You can use async and await in createUser function. You can alter your function by Using promise, We can able to either resolve or reject.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Within my getUnaddedCartItems() function I call loadCartItems() but I need access to the const addedItems within the .then() function. This all happens within my cart actions js file. Can someone please tell me how I can have access to an outer variable within an inner function?
export function loadCartItems() {
return (dispatch, getState) => {
dispatch({
type: types.LOAD_CART_PRODUCTS
});
return AsyncStorage.getItem(STORAGE_KEY_JWT_TOKEN).then((key) => {
return API.getCartItems(key)
.then((response) => {
return dispatch({
type: types.LOAD_CART_PRODUCTS_SUCCESS,
response
});
}).catch(err => {
console.log('Error retrieving cart products');
})
}).catch(err => {
console.log("Error retrieving cart items from local storage");
});
};
}
export function getUnaddedCartItems() {
return (dispatch, getState) => {
dispatch({
type: types.GET_UNADDED_ITEMS
});
return AsyncStorage.getItem(STORAGE_KEY_CART_ITEMS).then((result) => {
const addedItems = JSON.parse(result);
loadCartItems()(dispatch, getState).then((result) => {
const cartItems = result.response.products;
const unaddedCartItems = addedItems.filter((addedItem) => {
return cartItems.find(cartItem => cartItem.id !== addedItem.productId);
});
return unaddedCartItems;
}).catch(err => {
console.log('error: ', err);
});
}).catch(error => {
console.log('error: ', error);
});
};
}
Try removing the return keyword at the beginning of the line
return AsyncStorage.getItem(STORAGE_KEY_CART_ITEMS).then((result) => {
That might be preventing your inner function from running, if it ends the execution too early.
Also, I see you call
dispatch({
type: types.GET_UNADDED_ITEMS
});
at the beginning of the function,
but nowhere else are you dispatching an action afterwards. I think you need dispatch the appropriate action instead of return unaddedCartItems at the end of the inner then()
Update
Replacing
loadCartItems()(dispatch, getState).then((result) => {
const cartItems = result.response.products;
const unaddedCartItems = addedItems.filter((addedItem) => {
return cartItems.find(cartItem => cartItem.id !== addedItem.productId);
});
return unaddedCartItems;
}).catch(err => {
console.log('error: ', err);
});
With dispatch(loadCartItems()) may help achieve the desired outcome. Calling it without dispatch does not connect it to the redux store, which you are using inside the inner .then of loadCartItems
Sorry for my English, I hope you can understand me.
I use redux-thunk for async actions.
// actionA.js
export function actionA() {
return fetch('api call')
.then((data) => ({
type: 'actionA',
data: data
}))
}
// reducer.js
export function reducer(state, action) {
...
if(action.type === 'actionA') {
return {
...,
dataA: action.data,
...
};
}
}
// actionB.js
export function actionB() {
return (dispatch, getState) => {
if(!getState().dataA) {
dispatch(actionA());
}
doSomethingWithDataA(getState().dataA);
};
}
In some cases, I only need to dispatch actionA without dispatching actionB. But when I dispatching actionB, actionB will use dataA, and dataA is created by actionA. So in actionB, I will check if there is dataA in Store, if not, I will dispatch actionA first. But actionA is an async action, I can not get when actionA is done.
So how to deal with this problem?
Generally speaking, you want to do something when dataA is ready and actionB had happened. You can do it using middleware
function myMiddleware() {
return store => next => action => {
const dataA = store.getState().dataA;
if(store.getState().dataA && action.type === 'actionB') {
doSomethingWithDataA(dataA);
}
};
}
Then your actionB turns into the following
function lazyFetchDataA() {
return (dispatch, getState) => {
if(!getState().dataA) {
return dispatch(actionA());
} else {
return Promise.resolve();
}
}
}
export function actionB() {
return (dispatch, getState) => {
lazyFetchDataA().then(() => dispatch({type: 'actionB'}))
}
}
It looks difficult, but now you have little pieces of code in proper places that do your thing.
Probably you don't have to do such a difficult thing if you provide more information what your doSomethingWithDataA is, I would give a better answer.
This approach might help, note I will be using the fetch polyfill to return a promise for aysnc request.
function actionA(dataA) {
return {
type: 'actionA',
data: dataA
};
}
export function actionB() {
return (dispatch, getState) => {
if(!getState().dataA) {
return fetch('api call')
.then((data) => {
dispatch(actionA(data))
})
.catch((error) => {// handle error})
}
return doSomethingWithDataA(getState().dataA);
}
}