Javascript 如何将异步函数返回的值分配给变量

Javascript 如何将异步函数返回的值分配给变量,javascript,asynchronous,Javascript,Asynchronous,我是JavaScript新手,一直在努力阅读大量关于为什么这不起作用的文章。这是我的密码。我在这里也读了很多关于堆栈溢出的文章,但仍然感觉很密集 此外,如果我的标题没有意义,请建议编辑 listRef.listAll() .then(response => { let files = [] response.items.forEach(item => {

我是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变量是一个挂起的promise对象

我知道异步函数会返回一个承诺,所以当我调用getText时,我需要使用它。然后-我正在努力解决的问题是,我多次重构了这段代码: 如何将doc.data().text的值分配给稍后使用的变量?换句话说,var text如何可以是实际字符串而不是挂起的承诺对象

如果我替换了javascript,也可以在异步函数中学习javascript

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

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

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


谢谢你的帮助-还有任何文章来帮助我解释这将是伟大的!我一直在学习udemy的课程,但现在对此有点困惑

实际上,您正在为变量文本指定完整的承诺

替换

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


实际上,您正在为变量text

替换

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


当然,
文本
将成为一个
承诺
Promise.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
});
这当然会导致臭名昭著的“回调地狱”,在异步回调的层中有层。有关详细信息和缓解技术,请参阅


async/await
只是做同样事情的较新方法之一:MDN在这里有一篇优秀的文章:

当然
text
将是一个
承诺
Promise.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
});
这当然会导致臭名昭著的“回调地狱”,在异步回调的层中有层。有关详细信息和缓解技术,请参阅


async/await
只是做同样事情的较新方法之一:MDN在这里有一篇很好的文章:

好的,我和同事一起工作,找到了一个解决方案-它与本文相关 我在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

好的,我和工作中的某个人一起工作,找到了一个解决方案——这与本文有关 我在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

嘿,非常感谢您的回复!我知道我只能在一个异步函数中使用wait-所以我不确定这是否对我有效也很抱歉我想我看到了我的问题,但仍然不确定为什么我用完整的代码编辑了我的原始帖子-所以当我调用getText时,它在另一个函数中。然后方法嘿,非常感谢你的回复!我知道我只能在异步函数的内部使用wait-所以我不确定这是否对我有效也很抱歉我想我看到了我的问题,但仍然不确定为什么我用完整的代码编辑了我的原始帖子-所以当我调用getText时,它在另一个内部。然后方法谢谢你解释更多,我确实编辑了我的主要问题,以包括我调用异步函数的全部范围,我认为这可能是我的问题的一部分,如果我在外部调用它。然后在listRef上,一切都正常