【问题标题】:How to write an asynchronous function如何编写异步函数
【发布时间】:2021-12-19 12:06:40
【问题描述】:

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

下面是相关代码sn-p:

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 附加到它。

【问题讨论】:

    标签: javascript reactjs asynchronous async-await asynchronous-javascript


    【解决方案1】:

    您必须等待所有文件上传,然后您才能调用您的 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)
      })
    }
    

    【讨论】:

    • 它在控制台上抛出一个错误,显示“on 中的参数计数无效:预期在 1 到 4 个参数之间,收到 5 个。” "
    • 我使用 Promise.all 更改了我的答案,我使用它是因为它是您用例的最佳解决方案。
    【解决方案2】:

    说明

    函数和异步

    Async/Await 可以在函数开始的任何地方实现。函数可以写成以下形式:

    function name(){};
    function name() => {};
    

    要编写异步函数,您需要执行以下操作:

    async function name(){};
    

    虽然所有这些函数都称为函数,但要使函数在不调用它们的情况下运行,我们需要将它们转换为IIFE's,即立即调用函数执行。如果您想创建一个函数并立即执行它,您可以在() 中将该函数括起来并以(); 结束。

    (function () {})();
    

    如果我们简化一下:

    (() => {})();
    

    实现异步的过程如下:

    (async () => {})();
    

    等待

    await 运算符用于等待Promise,将await 放在使用承诺的表达式前面使其等待承诺。如果在不使用 Promise 的表达式前面使用它,则它是多余的,您的代码编辑器/IDE 会这样说。

    (async () => {
        const str = await 'some string';
        console.log(str);
    })();
    

    await 在这里是多余的,因为表达式 'some string' 与承诺无关,而是与字符串有关。

    (async () => {
      const myPromise = new Promise((resolve, reject) =>
        resolve('some string')
      );
    
      const str = await myPromise.then(x => console.log(x));
    })();
    

    await 在这里使用得当,因为表达式 myPromise 与输出字符串的承诺有关。

    实施

    我不能 100% 确定 api 在 promise 中是如何工作的,我建议你自己弄清楚,但这是我有根据的猜测:

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

    【讨论】:

    • 是的,但是你能帮我看看在这个例子中如何使用 await 关键字吗?
    • 老实说,我只是到处都在等待垃圾邮件,除非它告诉我在那里使用它是多余的,但我会更新答案。
    • 完成,如果有什么不清楚的地方请告诉我。
    猜你喜欢
    • 2021-12-31
    • 2011-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多