Reputation: 1482
In my react app, I have a function that somewhat simulates a call to an API. Is there a way I can synchronously run a callback function after this API request so that my data comes back in the order I sent it?
// the order in the array here matters
const wordTypeArr = ['type1', 'type2', 'type3']
// loop sequentially through array
wordTypeArr.forEach((v,i) => {
getRandomWordFromAPI(v, addWord)
})
//simulated "API" - can't modify this function
const getRandomWordFromAPI = (type, callback) => {
return setTimeout(function() {
callback(
type in dictionary ?
sample(dictionary[type]) :
null
);
}, (Math.random() * 750 + 250));
}
//callback runs after response - update the state
const addWord = (val) => {
const newState = wordList
newState.push(val)
setWordList(newState);
}
As you can see, the getRandomWordFromAPI
function returns a timeout function then executes the callback after the timeout asynchronously (out of order). This is undesirable, as my results must be in order.
Maybe I need to wrap addWord
in a promise? or something similar?
Upvotes: 1
Views: 602
Reputation: 8589
Changing that function would indeed be preferred as it'll make the code simpler to read. But since you can send getRandomWordFromAPI
a callback, you can send the resolve function as the callback instead of addWord() and then chain the addWord to the resolution.
I've put some parts of your code in comments, since we don't have the dictionary object and such, but the structure stays the same.
// the order in the array here matters
const wordTypeArr = ['type1', 'type2', 'type3'];
//simulated "API" - can't modify this function
const getRandomWordFromAPI = (type, callback) => {
return setTimeout(function() {
callback( type );
/*
callback(
type in dictionary ?
sample(dictionary[type]) :
null
);*/
}, (Math.random() * 750 + 250));
}
//callback runs after response - update the state
const addWord = (val) => {
console.log( val );
/*
const newState = wordList
newState.push(val)
setWordList(newState);
*/
}
const randomWords = wordTypeArr.map( word => {
return new Promise(( resolve, reject ) => {
getRandomWordFromAPI( word, resolve );
});
});
Promise.all( randomWords ).then( words => words.forEach( addWord ));
Upvotes: 2
Reputation: 18493
It's 2019, promises are in, callbacks are out. On a more serious note, here's how you can refactor your code to make it work the way you want to:
// mock the API
const getRandomWordFromAPI = (type, callback) => {
setTimeout(() => {
let word = `some-word-of-type-${type}`;
callback(word);
}, 1000)
}
// promisify the mocked API
const getRandomWordFromAPIPromise = (type) => new Promise(resolve => {
getRandomWordFromAPI(type, resolve);
});
// fetch all data asynchronously, in order
const wordTypeArr = ['type1', 'type2', 'type3'];
const promises = wordTypeArr.map(getRandomWordFromAPIPromise);
Promise.all(promises).then(words => {
// do whatever you want with the words
console.log(words)
});
Upvotes: 0