首页 > 解决方案 > 如何编写异步函数

问题描述

我正在做我的第一个反应网站,我需要帮助来编写一个异步 JavaScript 函数。在这里,我将用户输入文件上传到 Firebase 存储,然后向 API 发出发布请求以将数据存储在数据库中。但是,由于 firebase 上传需要一些时间将数据上传到其存储,API 请求发生在上传完成之前,因此数据不会上传到数据库。现在我知道我应该在这里使用 async await 关键字的承诺,但我不知道该怎么做。如果有人可以提供帮助,我将不胜感激。提前致谢!

这是相关的代码片段:

const save = (items) => {
  items.forEach((item) => {
    const fileName = new Date().getTime() + item.label + item.file.name;
    const uploadTask = storage.ref(`/items/${fileName}`).put(item.file);
    uploadTask.on(
      "state_changed",
      (snapshot) => {
        const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
        console.log("Upload is" + progress + "% done.");
      },
      (err) => {
        console.log(err)
      },
      () => {
        storage.ref("items").child(fileName).getDownloadURL().then((url) => {
          setSong((prev) => {
            return { ...prev, [item.label]: url };
          });
        });
      }
    );
  })
  console.log(song)
  axios.post("songs/create", song);
}

PS:这里的 items 是来自用户的输入文件数组,每个文件都有一个标签,这是 json 文档中属性的命名方式。setSong 是一个 useState 函数。这里 JSON 文档已经包含其他用户输入(不是文件),并且 setSong 方法用于将文件的 firebase URL 附加到它。

标签: javascriptreactjsasynchronousasync-awaitasynchronous-javascript

解决方案


您必须等待所有文件上传,然后才能调用 API,为此您应该使用Promise.all等待来解析所有文件:

  const save = items => {
  Promise.all(
    items.map(item => {
      return new Promise(resolve => {
        const fileName = new Date().getTime() + item.label + item.file.name
        const uploadTask = storage.ref(`/items/${fileName}`).put(item.file)
        uploadTask.on(
          'state_changed',
          snapshot => {
            const progress =
              (snapshot.bytesTransferred / snapshot.totalBytes) * 100
            console.log('Upload is' + progress + '% done.')
          },
          err => {
            console.log(err)
          },
          () => {
            storage
              .ref('items')
              .child(fileName)
              .getDownloadURL()
              .then(url => {
                setSong(prev => {
                  return { ...prev, [item.label]: url }
                })
                resolve({[item.label]: url})
              })
          }
        )
      })
    })
  ).then((res) => {
    const song = {}
    res.forEach(item => {
      return {
        ...song,
        ...item
      }
    })
    axios.post('songs/create', song)
  })
}

推荐阅读