【问题标题】:how do I assign a returned value from an async function to a variable如何将异步函数的返回值分配给变量
【发布时间】:2020-12-09 08:14:13
【问题描述】:

我是 JavaScript 新手,并且一直在尝试阅读很多关于为什么它不起作用。这是我的代码。我也在这里阅读了一些关于堆栈溢出的文章,但仍然感觉很密集

另外,如果我的标题没有意义,请提出修改建议

 listRef.listAll()
            .then(response => {
                let files = []  
                response.items.forEach(item => {
                    
                  var text
                  getText(item.name).then(res=>{text = res});
                    
                    const id = {uid: guid()}
                    const url = item.getDownloadURL().then(url => {return url} )
                    const gsurl = `gs://archivewebsite.appspot.com/${folder}/${item.name}`
                    files.push({...item, name:item.name, url, gsurl, id:id.uid, text})
                
                    });
                    this.files = files;
                  })
            .catch(error => console.log(error));


  async function getText(docID) {
        var docRef = firestore.collection("recipes").doc(docID);
        let doc = await docRef.get()
        if (doc.exists){
           return doc.data().text
         }
}

该代码“有效”,因为它将响应记录到控制台,但 text 变量是待处理的承诺对象。

我知道异步函数会返回一个承诺,所以当我调用 getText 时,我需要使用 .then - 我正在努力解决并多次重构此代码的是: 如何将 doc.data().text 的值分配给稍后使用的变量,换句话说,var text 如何成为实际字符串而不是待处理的承诺对象

如果我替换的话,也是为了我自己在异步函数中学习 javascript

if (doc.exists){
           return doc.data().text
         }

 if (doc.exists){
           return Promise.resolve(doc.data().text)
         }

我在 console.log 中得到相同的结果 - 这是预期的吗? return 只是处理程序解决承诺的简写吗?

我还将这段代码重构为非异步的,我得到了相同的结果,其中我的 var 文本基本上是一个待处理的承诺,而不是解析的数据

感谢您的帮助 - 任何有助于向我解释这一点的文章都会很棒!我一直在学习udemy的课程,但现在对此有点困惑

【问题讨论】:

    标签: javascript asynchronous


    【解决方案1】:

    实际上,您正在将完整的承诺分配给变量 text

    替换

    var text = getText(item.name).then(res=>console.log(res))
    

    通过

    var text = await getText(item.name);
    

    var text
    getText(item.name).then(res=>{text = res});
    

    【讨论】:

    • 嘿,非常感谢您的回复!我知道我只能在异步函数中使用 await - 所以我不确定这是否对我有用也很抱歉,我想我看到了我的问题,但仍然不确定为什么我用完整的代码编辑了我的原始帖子- 所以当我在另一个 .then 方法中调用 getText 时
    • 我正在尝试与上一个示例类似的方法,但我希望将返回值分配为const。但这是不可能的,有没有其他方法可以做到这一点?如果我尝试像第一个示例一样使用let och const 我得到Promise {<fulfilled>: undefined} console.log 已解决但不是分配。
    • @Oortone 你可以尝试使用 async-await const text = await getText(item.name);
    • @NavKumarV:我得到:"Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules"。也许如果我重构整个代码并使用模块和/或封装,我不确定。
    • @Oortone 我猜你正在使用await 没有function being declared as async。请转至developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
    【解决方案2】:

    当然text 将成为PromisePromise.then() 总是返回 Promise

    考虑这段代码:

    function doA(n) {
      // do something here...
      console.log("A" + n);
    }
    asnyc function doB(n) {
      // do something here...
      console.log("B" + n);
    }
    
    doA(1);
    doA(2);
    doB(3); // async
    doA(4);
    doB(5); // async
    doB(6); // async
    doA(7);
    

    你期望输出是什么?

    • 1、2、4 和 7 将始终按顺序排列,因为它们是同步执行的。
    • 3 永远不会在 1 和 2 之前打印。同样,5 和 6 永远不会在 1、2 和 4 之前打印。
    • 但是,3、5 和 6 可以按任意顺序打印,因为异步函数一旦创建就不能保证执行顺序。
    • 此外,4 可以在 3 之前打印。同样,7 可以在 5 和 6 之前打印。

    基本上,将异步函数视为独立运行的并行任务(虽然不是真的;单线程 JS 仅模拟这种行为)。它可以随时返回(履行/拒绝)。出于这个原因,您不能简单地将异步函数的返回值分配给使用同步代码的变量 - 不能保证该值在同步执行时(并且可能不)可用。


    因此需要将所有需要设置text值的代码放入Promise的回调块中,如下所示:

    getText(item.name).then((text) => {
      // put everything that uses text here
    });
    

    这当然会导致臭名昭著的“回调地狱”,在异步回调层中存在层。有关详细信息和缓解技术,请参阅 http://callbackhell.com

    async/await 只是做同样事情的新方法之一:MDN 在这里有一篇很棒的文章:https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await

    【讨论】:

    • 感谢您进一步解释这一点,我确实编辑了我的主要问题以包括我调用异步函数的全部范围,我认为这可能是我的问题的一部分,如果我在外面调用它listRef 上的 .then 一切正常
    【解决方案3】:

    好的,我在工作中与某人合作并找到了解决方案 - 它与这篇文章有关 https://stackoverflow.com/a/37576787/5991792 我在 for each 循环中使用了异步函数

    重构的代码在这里

    async function buildFiles(){
      let items = await listRef.listAll()
      let files = []
      for (const item of item.items){
        const text = await getText(item.name)
        const url = await item.getDownloadURL()
        const gsurl = `gs://archivewebsite.appspot.com/${folder}/${sermon.name}`
        files.push({...item, name:item.name, url, gsurl, text})  
    }
    return files
    }
    
    
     async function getText(docID) {
            var docRef = firestore.collection("recipies").doc(docID);
            let doc = await docRef.get()
            if (doc.exists){return await doc.data().text}}
    
    
    
    buildFiles().then(res=>this.files = res)
    

    还要感谢@cyqsimon 和@Nav Kumar V

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-05
      • 2021-03-04
      • 1970-01-01
      • 2023-03-06
      相关资源
      最近更新 更多