I am writing a lyrics app, that saves a song book and the songs in that book to an SQL lite database. I am mapping through an array of books and calling an API to save the book's songs. I want the saveBook() function to fully complete before moving on to the saveBookSong() function. I have tried async await but it is not working. Any help would be greatly appreciated, Thanks, Sam
useEffect( () => {
Books.map ((book) =>{
saveBook(book)
saveBookSong(book.songId.toString())
})
function saveBook(book) {
db.transaction( (tx) => {
tx.executeSql(
`INSERT INTO books (id, description, img, name, song_id)
VALUES (?, ?, ?, ?, ?)`,
[book.id, book.description, book.img, book.name, book.songId],
() => {
console.log("Saved!!", book.id)
}
);
});
}
function saveBookSong(id) {
axios
.get(`songs/${id}`)
.then((bookSong) => {
bookSong.data.forEach((song) => {
db.transaction((tx) => {
tx.executeSql(
`INSERT INTO songs (id, book, book_id, data, int_song_number, lang, search_title, song_number, title, url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[
song.id,
song.book,
song.bookId,
song.data,
song.intSongNumber,
song.lang,
song.searchTitle,
song.songNumber,
song.title,
song.url,
],
() => {
}
);
});
});
})
.catch((err) => {
console.log(err);
});
}
CodePudding user response:
If I'm not mistaken .map() is not async aware. So I would suggest two solutions to your problem:
1)
useEffect( () => {
Books.map ((book) =>{
saveBook(book).then(() => saveBookSong(book.songId.toString()))
}, [Books])
In this case you go with then after first function is executed.
2)
useEffect( () => {
const manageBooks = async() => {
for (book of Books) {
await saveBook(book);
await saveBookSong(book.songId.toString());
}
}
}, [Books])
You can go with async for loop which will wait for executing first task before moving to another one
CodePudding user response:
I'm assuming you want all the books to be saved in parallel. I would do this:
useEffect( () => {
const bookSavings = Books.map(async (book) =>{
await saveBook(book)
await saveBookSong(book.songId.toString())
});
// If you want to wait for all books to be saved
await Promise.all(bookSavings);
// ... Whatever else you want to do in your useEffect
}, [Books])
Alright, but you can't simply await
saveBook
and saveBookSong
because they are not async
functions. Let's move forward.
Under the hood, async functions are just functions that return promises. We can actually await
any promise. Therefore, you can make saveBook
and saveBookSong
awaitable by wrapping them in promises, like this:
function saveBook(book) {
return new Promise((resolve) => {
db.transaction( (tx) => {
tx.executeSql(
`INSERT INTO books (id, description, img, name, song_id)
VALUES (?, ?, ?, ?, ?)`,
[book.id, book.description, book.img, book.name, book.songId],
() => {
console.log("Saved!!", book.id)
resolve(); // <- THIS IS IMPORTANT
}
);
});
});
}
I will leave wrapping the other function in a promise to you. The idea is that you call resolve()
when you want the await
ing caller to proceed.
I hope this helps!