I am trying to fetch data from Algolia database (index.search is similar to fetch) in useEffect,but then I find the order it execute is not the way I think.I console "queryNews1", "queryNews2", ..."queryNews6" in async function queryNews(), and I think they will sequentially print out in console(see image below). But I find that after queryNews2, it "jump out" of queryNews() but execute the code outside queryNews(), after console.log("5"), it go back to execute "queryNews3".
I guess it's an asychronous issue, so I wrap queryNews() inside an another async function const getData = async () => { await queryNews(keyword); };
and call getData(), but it's still execute in wrong way.Why and does anybody know how to fix that??
Sorry for my bad English!
mobile in the console is writing in articleState.map(() => { console.log("mobile"); return (); });
const [articleState, setArticles] = useState<ArticleType[]>([]);
useEffect(() => {
console.log("1");
if (windowResized === "large" || windowResized === undefined) return;
let isFetching = false;
let isPaging = true;
let paging = 0;
console.log("2");
async function queryNews(input: string) {
console.log("queryNews1");
isFetching = true;
setIsLoading(true);
setSearchState(true);
setPageOnLoad(true);
console.log("queryNews2");
const resp = await index.search(`${input}`, {
page: paging,
});
console.log("queryNews3");
const hits = resp?.hits as ArticleType[];
setTotalArticle(resp?.nbHits);
console.log("queryNews4");
setArticles((prev) => [...prev, ...hits]);
console.log("queryNews5");
setIsLoading(false);
console.log("queryNews6");
paging = paging 1;
if (paging === resp?.nbPages) {
isPaging = false;
setScrolling(true);
return;
}
console.log("queryNews7");
isFetching = false;
setSearchState(false);
setPageOnLoad(false);
console.log("queryNews8");
}
console.log("3");
async function scrollHandler(e: WheelEvent) {
if (window.innerHeight window.scrollY >=
document.body.offsetHeight - 100) {
if (isFetching || !isPaging) return;
console.log("scrollHandler");
getData();
}
}
const getData = async () => {
await queryNews(keyword);
};
getData()
console.log("4");
window.addEventListener("wheel", scrollHandler);
console.log("5");
return () => {
window.removeEventListener("wheel", scrollHandler);
};
}, [keyword, setSearchState, windowResized]);
CodePudding user response:
Thanks to kim3er, that really help.
But simillar situation happened again when I scroll, it console.log("queryNews2"), and then it console "mobile", which means it render the component again, and then go back to queryNews() to finish execute the rest of the function?Why didn't it wait while I already put all the code in
getData().then(() => {
console.log("6");
window.addEventListener("wheel", scrollHandler);
console.log("7");
});
Thanks!!
(stack overflow suddenly said I can't embed image now so I paste an image link instead) https://imgur.com/a/lDKEzxy
useEffect(() => {
console.log("1");
if (windowResized === "large" || windowResized === undefined) return;
let isFetching = false;
let isPaging = true;
let paging = 0;
console.log("2");
async function queryNews(input: string) {
console.log("queryNews1");
isFetching = true;
setIsLoading(true);
setSearchState(true);
setPageOnLoad(true);
console.log("queryNews2");
const resp = await index.search(`${input}`, {
page: paging,
});
console.log("queryNews3");
const hits = resp?.hits as ArticleType[];
setTotalArticle(resp?.nbHits);
console.log("queryNews4");
setArticles((prev) => [...prev, ...hits]);
console.log("queryNews5");
setIsLoading(false);
console.log("queryNews6");
paging = paging 1;
if (paging === resp?.nbPages) {
isPaging = false;
setScrolling(true);
return;
}
console.log("queryNews7");
isFetching = false;
setSearchState(false);
setPageOnLoad(false);
console.log("queryNews8");
}
console.log("3");
async function scrollHandler(e: WheelEvent) {
if (
window.innerHeight window.scrollY >=
document.body.offsetHeight - 100
) {
if (isFetching || !isPaging) return;
console.log("scrollHandler");
getData().then(() => {
console.log("6");
window.addEventListener("wheel", scrollHandler);
console.log("7");
});
}
}
const getData = async () => {
await queryNews(keyword);
};
getData().then(() => {
console.log("4");
window.addEventListener("wheel", scrollHandler);
console.log("5");
});
return () => {
window.removeEventListener("wheel", scrollHandler);
};
}, [keyword, setSearchState, windowResized]);
CodePudding user response:
You're calling getData()
without awaiting it. Because of this, it'll run parallel to the top level useEffect
code. This isn't an issue, if getData()
is the last line of code in the function.
But if you do need that initial getData()
to complete before hitting console.log("4");
, you could do this:
getData()
.then(() => {
console.log("4");
window.addEventListener("wheel", scrollHandler);
console.log("5");
});
From console.log("4");
will run after the call to getData()
.
Clarifier on async
and .then()
With this function in mind:
const doSomething = async () => {
// Do something async
console.log('during');
});
The following:
const asyncFunc = async () => {
console.log('before');
await doSomething();
console.log('after');
});
Is equivalent to:
const asyncFunc = () => {
console.log('before');
doSomething()
.then(() => {
console.log('after');
});
});
Either will return:
before
during
after
However, if you used:
const syncFunc =() => {
console.log('before');
doSomething();
console.log('after');
});
Becasue I have not awaited doSomething()
, I have created a race condition, whereby during
and after
could be returned in a different order depending on how long it took the async code to complete. Because the syncFunc
script will continue running as soon as doSomething
has been called (but crucially, has not finished).
So you could get:
before
after
during
Wrapping await queryNews(keyword);
in another function called getData()
does not make the function synchronous, it just means that you don't have to keep typing in the keyword
parameter. You still need to await getData()
, in order to ensure completion before continuing.