草庐IT

javascript - 异步函数不返回值,但 console.log() 执行 : how to do?

coder 2024-07-28 原文

这个问题在这里已经有了答案:





How to return the response from an asynchronous call

(42 个回答)


3年前关闭。




我有一个 es6 类,带有 init()方法负责获取数据,转换数据,然后更新类的属性 this.data使用新转换的数据。
到现在为止还挺好。
类本身还有另一个 getPostById()方法,只是做它听起来像的事情。这是该类的代码:

class Posts {
  constructor(url) {
    this.ready = false
    this.data = {}
    this.url = url
  }
  async init() {
      try { 
        let res = await fetch( this.url )
        if (res.ok) {
            let data = await res.json()

          // Do bunch of transformation stuff here

          this.data = data
          this.ready = true
            return data
        }
      } 
      catch (e) { 
         console.log(e)
      }
  }
  getPostById(id){
     return this.data.find( p => p.id === id )
  }
}  

直截了当,除了我有一个 async/await init() 中的机制方法。
现在,此代码将正常工作:
let allPosts = new Posts('https://jsonplaceholder.typicode.com/posts')

allPosts.init()
        .then( d => console.log(allPosts.getPostById(4)) )
// resulting Object correctly logged in console

但它只会打印到控制台中:
我该如何使用 allPosts.getPostById(4)作为 return一个函数?

喜欢:
let myFunc = async () => {
   const postId = 4
   await allPosts.init()  // I need to wait for this to finish before returning

   // This is logging correct value
   console.log( 'logging: ' + JSON.stringify(allPosts.getPostById( postId ), null, 4) )

   // How can I return the RESULT of allPosts.getPostById( postId ) ???
   return allPosts.getPostById( postId )
}
myFunc()返回 Promise但不是最终值。我已经阅读了几篇关于这个主题的相关文章,但它们都给出了日志记录的例子,再也没有回来。

Here is a fiddle包括两种处理方式 init() :使用 Promise并使用 async/await .无论我尝试什么,我都无法使用 getPostById(id) 的最终值.

这个帖子的问题是:我如何创建一个函数来返回 getPostById(id) 的值?

编辑:

很多很好的答案试图解释关于主执行循环的 Promises 是什么。
经过大量视频和其他好的阅读,以下是我现在的理解:

我的函数 init()正确返回。然而,在主事件循环中:它返回 一个 promise ,那么我的工作就是从 中获取这个 Promise 的结果。有点 并行循环(不是新的真正线程)。为了从并行循环中捕获结果,有两种方法:
  • 使用 .then( value => doSomethingWithMy(value) )
  • 使用 let value = await myAsyncFn() .现在这是愚蠢的打嗝:

  • await can only be used within an async function :p



    因此本身返回一个 Promise,可用于 await应该嵌入到 async 中函数,可用于 await等等...

    这意味着我们不能真正等待 Promise:相反,我们应该无限期地捕获并行循环:使用 .then()async/await .

    谢谢您的帮助 !

    最佳答案

    至于你的评论;我将其添加为答案。

    您用 JavaScript 编写的代码在一个线程上运行,这意味着如果您的代码实际上可以等待某些东西,它将阻止您执行任何其他代码。 JavaScript 的事件循环在 this video 中有很好的解释如果您喜欢阅读 this page .

    在浏览器中阻止代码的一个很好的例子是 alert("cannot do anything until you click ok"); .警报会阻止所有内容,用户甚至无法滚动或单击页面中的任何内容,您的代码也会阻止执行。

    Promise.resolve(22)
    .then(x=>alert("blocking")||"Hello World")
    .then(
      x=>console.log(
        "does not resolve untill you click ok on the alert:",
        x
      )
    );
    

    在控制台中运行它,你就会明白我所说的阻塞是什么意思。

    当您想做一些需要时间的事情时,这会产生问题。在其他框架中,您会使用线程或进程,但 JavaScript 中没有这样的东西(从技术上讲,节点中有 web worker 和 fork,但这是另一回事,通常比使用异步 api 复杂得多)。

    因此,当您想发出 http 请求时,您可以使用 fetch但是 fetch 需要一些时间才能完成,并且您的函数不应阻塞(必须尽快返回某些内容)。这就是 fetch 返回一个 promise 的原因。

    请注意,fetch 是由浏览器/节点实现的,并且确实在另一个线程中运行,只有您编写的代码在一个线程中运行,因此启动大量仅运行您编写的代码的 promise 不会加快任何速度,但会并行调用 native 异步 api。

    在 promise 之前异步代码使用回调或返回一个可观察对象(如 XmlHttpRequest),但让我们介绍一下 promise,因为无论如何您都可以将更传统的代码转换为 promise。

    promise 是具有 then 的对象。函数(还有一堆东西是糖,但做同样的事情),这个函数需要 2 个参数。
  • 解析处理程序:当 promise 解决(没有错误并完成)时将由 promise 调用的函数。该函数将传递一个带有解析值的参数(对于 http 请求,这通常是响应)。
  • 拒绝处理程序:当 promise 拒绝(有错误)时由 promise 调用的函数。这个函数将传递一个参数,这通常是错误或拒绝的原因(可以是字符串、数字或任何东西)。

  • 将回调转换为 promise 。

    传统的 api(尤其是 nodejs api)使用回调:
    traditionalApi(
      arg
      ,function callback(err,value){ 
        err ? handleFail(err) : processValue(value);
      }
    );
    

    这使得程序员很难以线性方式(从上到下)捕获错误或处理返回值。尝试与错误处理并行或节流并行处理事情变得更加不可能(无法阅读)。

    您可以使用 new Promise 将传统的 api 转换为 promises
    const apiAsPromise = arg =>
      new Promise(
        (resolve,reject)=>
          traditionalApi(
            arg,
            (err,val) => (err) ? reject(err) : resolve(val)
          )
      )
    

    异步等待

    这就是所谓的 promise 的语法糖。它使 Promise 消费函数看起来更传统且更易于阅读。也就是说,如果您喜欢编写传统代码,我认为组合小函数更容易阅读。例如,你能猜出这是做什么的吗?:
    const handleSearch = search =>
      compose([
        showLoading,
        makeSearchRequest,
        processRespose,
        hideLoading
      ])(search)
      .then(
        undefined,//don't care about the resolve
        compose([
          showError,
          hideLoading
        ])
      );
    

    无论如何;足够的咆哮。重要的是要了解async await实际上并没有启动另一个线程,async函数总是返回一个 promise 和 await实际上并不阻塞或等待。这是 someFn().then(result=>...,error=>...) 的语法糖看起来像:
    async someMethod = () =>
      //syntax sugar for:
      //return someFn().then(result=>...,error=>...)
      try{
        const result = await someFn();
        ...
       }catch(error){
         ...
       }
    }
    

    示例始终显示 try catch但你不需要这样做,例如:
    var alwaysReject = async () => { throw "Always returns rejected promise"; };
    alwaysReject()
    .then(
      x=>console.log("never happens, doesn't resolve")
      ,err=>console.warn("got rejected:",err)
    );
    

    抛出的任何错误或 await返回一个被拒绝的 promise 将导致异步函数返回一个被拒绝的 promise (除非你试图捕获它)。很多时候希望让它失败并让调用者处理错误。

    当您希望 promise 成功并为被拒绝的 promise 设置特殊值时,可能需要捕获错误,以便您可以稍后处理它,但 promise 在技术上不会拒绝,因此将始终解决。

    一个例子是 Promise.all ,这需要一个 promise 数组并返回一个新的 promise ,该 promise 解析为一个已解析值的数组 或当其中任何一个拒绝时拒绝 .您可能只想获取所有 promise 的结果并过滤掉被拒绝的结果:
    const Fail = function(details){this.details=details;},
    isFail = item => (item && item.constructor)===Fail;
    Promise.all(
      urls.map(//map array of urls to array of promises that don't reject
        url =>
          fetch(url)
          .then(
            undefined,//do not handle resolve yet
            //when you handle the reject this ".then" will return
            //  a promise that RESOLVES to the value returned below (new Fail([url,err]))
            err=>new Fail([url,err])
          )
      )
    )
    .then(
      responses => {
        console.log("failed requests:");
        console.log(
          responses.filter(//only Fail type
            isFail
          )
        );
        console.log("resolved requests:");
        console.log(
          responses.filter(//anything not Fail type
            response=>!isFail(response)
          )
        );
      }
    );
    

    关于javascript - 异步函数不返回值,但 console.log() 执行 : how to do?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47664598/

    有关javascript - 异步函数不返回值,但 console.log() 执行 : how to do?的更多相关文章

    1. ruby-openid:执行发现时未设置@socket - 2

      我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass

    2. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

      为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

    3. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

      exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

    4. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

      我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

    5. ruby - Chef 执行非顺序配方 - 2

      我遵循了教程http://gettingstartedwithchef.com/,第1章。我的运行list是"run_list":["recipe[apt]","recipe[phpap]"]我的phpapRecipe默认Recipeinclude_recipe"apache2"include_recipe"build-essential"include_recipe"openssl"include_recipe"mysql::client"include_recipe"mysql::server"include_recipe"php"include_recipe"php::modul

    6. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

      我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

    7. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

      我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

    8. ruby - 在 Ruby 中有条件地定义函数 - 2

      我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

    9. ruby - 为什么 Ruby 的 each 迭代器先执行? - 2

      我在用Ruby执行简单任务时遇到了一件奇怪的事情。我只想用每个方法迭代字母表,但迭代在执行中先进行:alfawit=("a".."z")puts"That'sanalphabet:\n\n#{alfawit.each{|litera|putslitera}}"这段代码的结果是:(缩写)abc⋮xyzThat'sanalphabet:a..z知道为什么它会这样工作或者我做错了什么吗?提前致谢。 最佳答案 因为您的each调用被插入到在固定字符串之前执行的字符串文字中。此外,each返回一个Enumerable,实际上您甚至打印它。试试

    10. ruby - Ruby 中的隐式返回值是怎么回事? - 2

      所以我开始关注ruby​​,很多东西看起来不错,但我对隐式return语句很反感。我理解默认情况下让所有内容返回self或nil但不是语句的最后一个值。对我来说,它看起来非常脆弱(尤其是)如果你正在使用一个不打算返回某些东西的方法(尤其是一个改变状态/破坏性方法的函数!),其他人可能最终依赖于一个返回对方法的目的并不重要,并且有很大的改变机会。隐式返回有什么意义?有没有办法让事情变得更简单?总是有返回以防止隐含返回被认为是好的做法吗?我是不是太担心这个了?附言当人们想要从方法中返回特定的东西时,他们是否经常使用隐式返回,这不是让你组中的其他人更容易破坏彼此的代码吗?当然,记录一切并给出

    随机推荐