Javascript 在过滤前过早返回函数并减少完成时间

Javascript 在过滤前过早返回函数并减少完成时间,javascript,actions-on-google,dialogflow-es,Javascript,Actions On Google,Dialogflow Es,我有以下函数,它应该检查一个数组,对一些值求和,然后返回一个带和的字符串(对于Dialogflow/Google Assistant): 但是,问题是,该函数只返回您花费了0的,其中0是reduce函数中设置的初始值,但总和实际上是30。它似乎并不等待reduce完成。 这里有什么问题?app.ask()会将响应发送回用户(并且,由于您使用的是ask()而不是tell(),因此也会说保持麦克风打开)。它返回的正是Express'Response.send()method返回的内容 它不会将字符串

我有以下函数,它应该检查一个数组,对一些值求和,然后返回一个带和的字符串(对于Dialogflow/Google Assistant):

但是,问题是,该函数只返回您花费了0的
,其中0是reduce函数中设置的初始值,但总和实际上是30。它似乎并不等待reduce完成。
这里有什么问题?

app.ask()
会将响应发送回用户(并且,由于您使用的是
ask()
而不是
tell()
,因此也会说保持麦克风打开)。它返回的正是Express'
Response.send()
method返回的内容

它不会将字符串返回给调用函数


如果需要该值,此时不应调用
app.ask()
。只需返回值(或最终解析为该值的承诺)并调用
app.ask()

恭喜,您发现了人们在使用
承诺时存在的一个最大误解:承诺可以将可预测的值返回到其调用上下文

正如我们所知,我们传递给
newpromise(callback)
构造函数的回调会收到两个参数,每个回调本身都会被调用

  • resolve(result)-异步操作的结果
  • 拒绝(错误)-如果操作失败,则出现错误情况
  • 假设我们定义了一个简单函数,该函数返回一个包装在对象中的字符串消息:

    /**
     * @typedef {Object} EchoResult
     * @property {String} message
     *
     * @example
     *    {message: 'this is a message'}
     */
    
    /**
     * Wraps a message in a EchoResult.
     * @param  {String} msg - the message to wrap
     * @return {EchoResult}
     */
    const echo = function (msg) {
      return {
        message: msg
      }
    }
    
    打电话

    const result = echo('hello from echo!')
    
    console.log(result)
    
    我们可以期待

    { message: 'hello from echo!'}
    
    要打印到控制台

    到目前为止还不错

    现在让我们做同样的事情,用承诺来包装:

    /**
     * Wraps a msg in an object.
     * @param  {String} msg - the message to wrap
     * @return {Promise.<EchoResult>}
     */
    const promised_echo = function (msg) {
      return new Promise((resolve, reject) => {
        resolve({message: msg})
      })
    }
    
    我们可以期待

    { message: 'hello from promised echo!'}
    
    { result: undefined }
    
    要打印到控制台

    呜呜*凯旋的大张旗鼓,我们用了承诺

    现在是让很多人困惑的部分

    您希望我们的
    承诺的\u echo
    函数返回什么?让我们看看

    const result =
      promised_echo('returns a Promise!')
    
    console.log({result})
    
    是的!它返回一个
    承诺

    { result: Promise { <pending> } }
    
    哦!!惊喜它还返回一个承诺

    { result: Promise { <pending> } }
    
    您的承诺链中的最后一个启用尝试返回调用您的
    app.ask()
    函数的结果,正如我们所看到的,这是无法完成的

    因此,为了能够使用传递给thenable的值,您似乎只能在thenable回调本身内部使用它

    这是所有承诺中最大的误解

    “但是,等等,!”,正如清晨的电视小贩所说,“还有更多!”

    另一种启用方式是
    .catch()
    函数,它是什么

    这里我们发现了另一个惊喜

    它的工作原理与我们发现的
    .then()
    函数完全相同,因为它不能返回值,而且不能抛出一个错误,我们可以用它来定位代码中的问题

    让我们重新定义
    承诺的\u echo
    失败得相当不快:

    const bad_promised_echo = function (msg) {
      return new Promise((resolve, reject) => {
        throw new Error(`FIRE! FAMINE! FLEE!! IT'S TEOTWAWKI!`)
      })
    }
    // TEOTWAWKI = The End Of The World As We Know It!
    
    这样,跑,

    bad_promised_echo('whoops!')
    
    将在以下情况下失败:

    (node:40564) UnhandledPromiseRejectionWarning: 
      Unhandled promise rejection (rejection id: 1): 
        Error: FIRE! FAMINE! FLEE!! IT'S TEOTWAWKI!
    
    等等,什么

    我的踪迹到哪里去了

    我需要我的stacktrace,这样我就可以确定错误发生在我的代码中的什么地方!(“我需要我的痛苦!这就是我,我的原因!”-由James T.Kirk转述。)

    我们丢失了stacktrace,因为从Promise的回调函数的角度来看,我们没有可以从中派生它的运行时上下文

    哦,我知道了!我将使用另一种启用,即
    .catch()
    函数捕获错误

    你会这么想的,但是没有骰子!因为我们仍然缺少任何运行时上下文。这次我将为你保存示例代码。相信我,或者更好的是,你自己试试看!)

    承诺在程序控制的常规流程之外运行,在异步运行的昏暗世界中,我们期望在未来某个时候完成的事情

    (这就是我摆出精通科学的姿势的地方,我抬起手臂,指着手指,凝视着远方,不祥地吟诵着……)

    未来!

    *队列swoopy Scfi theremin music*

    这是异步编程真正有趣但令人困惑的地方,我们运行函数是为了实现某些目标,但我们无法知道它们何时完成,这就是异步性的本质

    考虑以下代码:

    request.get('http://www.example.com').then(result => {
      // do something with the result here
    })
    
    你预计什么时候能完成

    取决于几个不可知的因素,如网络状况、服务器负载等,无法知道get请求何时完成,除非您具有某种可怕的预感知能力

    为了说明这一点,让我们回到echo示例,通过稍微延迟承诺的解决,在函数中注入一点“不可知性”:

    let result
    
    const promised_echo_in_the_future = function (msg) {
      return new Promise(function(resolve, reject) {
        setTimeout(() => { // make sure our function completes in the future
          result = msg
          resolve({
            message: msg
          })
        }, 1) // just a smidge
      })
    }
    
    然后运行它(我想你现在知道我要做什么了。)

    由于在控制台日志函数尝试引用它之后,承诺的解析只发生了一点点,因此我们可以预期

    { message: 'hello from promised echo!'}
    
    { result: undefined }
    
    要在控制台上打印

    好的,这需要消化很多,我赞扬你的耐心

    我们还有一件事要考虑。

    如何使用
    async/await
    ?我们可以使用它们来公开异步函数的解析值,以便在该函数的上下文之外使用它吗

    不幸的是,没有

    因为async/await实际上只是一种语法糖,通过使承诺看起来是同步操作的,来隐藏承诺的复杂性

    const async_echo = async function (msg) {
      let res = await promised_echo(msg)
      return res
    }
    
    let result = async_echo();
    
    console.log({result})
    
    将预期结果打印到控制台

    { result: Promise { <pending> } }
    
    {result:Promise{}
    
    我希望这能把事情弄清楚一点

    使用承诺时,我能给出的最佳建议是:

    始终在承诺链本身的回调中使用调用承诺链的任何已解析结果,并以相同的方式处理在承诺链中创建的任何错误<
    { result: undefined }
    
    const async_echo = async function (msg) {
      let res = await promised_echo(msg)
      return res
    }
    
    let result = async_echo();
    
    console.log({result})
    
    { result: Promise { <pending> } }