Home > Software design >  React Native JS Synchronous
React Native JS Synchronous

Time:10-28

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 awaiting caller to proceed.

I hope this helps!

  • Related