actions-on-google v1->v2 migration: "Error: Response has already been sent"

actions-on-google v1->v2 migration: "Error: Response has already been sent"

我正在从 v1 迁移到 v2 alpha,但出现以下错误:

Error: Response has already been sent. Is this being used in an async call that was not returned as a promise to the action/intent handler?
at DialogflowConversation.add (/user_code/node_modules/actions-on-google/dist/service/actionssdk/conversation/conversation.js:51:19)
at DialogflowConversation.close (/user_code/node_modules/actions-on-google/dist/service/actionssdk/conversation/conversation.js:66:21)
at Request.err [as _callback] (/user_code/index.js:1141:20)
at Request.self.callback (/user_code/node_modules/request/request.js:186:22)
at emitTwo (events.js:106:13)
at Request.emit (events.js:191:7)
at Request.<anonymous> (/user_code/node_modules/request/request.js:1163:10)
at emitOne (events.js:96:13)
at Request.emit (events.js:188:7)
at IncomingMessage.<anonymous> (/user_code/node_modules/request/request.js:1085:12)

来源:

        try {
          conv.close(new SimpleResponse({
            // Sending the details to the user
            speech: textToSpeech1,
            text: displayText1
          }))
        } catch (err) {
            if(err instanceof Error) {
                console.error(err);
            } else {
                console.error(new Error(err));
            }
        }

代码位置:https://github.com/BTS-CM/BeyondBitshares-Firebase/blob/master/index_v2.js#L1141

我不明白为什么会出现上述错误,此时我还没有向用户发送响应。也许对外部 REST API(使用请求库)的请求可以算作 'Response' 可能导致此错误?

也许这是 conv.close() 元素中简单响应的格式?

知道为什么会出现此错误吗?

干杯


编辑:

我认为我需要开始使用像这个 v2 片段这样的异步代码和承诺吗?它似乎在 REST API 查询开始之前响应空白..

app.intent('tell_greeting', conv => {
  // Return a promise to do async asks, will send everything when promise     resolves
  return new Promise(resolve => setTimeout(() => resolve(), 1000))
    .then(() => {
      conv.ask('Hi')
    })
})

为鼓励最佳实践,v2 客户端库需要使用 Promises 来执行异步请求。

响应生成的工作原理是它将在 IntentHandler 函数执行结束时生成并发送响应,除非函数 returns a Promise,然后它将等待 Promise 解决然后发送响应。

您可以查看 v2.0.0-alpha 分支中包含的 Name Psychic sample 以查看工作示例:

app.intent('handle_permission', (conv, params, permissionGranted) => {
  // ...
    return coordinatesToCity(coordinates.latitude, coordinates.longitude)
      .then(city => {
        conv.user.storage.location = city
        showLocationOnScreen(conv)
      })
  // ...
}

coordinatesToCity 这里是一个 returns 一个 Promise.

的函数

showLocationOnScreen 这里是调用 conv.askconv.close 并生成响应的函数。

const showLocationOnScreen = conv => {
  const capability = 'actions.capability.SCREEN_OUTPUT'
  if (conv.surface.capabilities.has(capability) ||
    !conv.available.surfaces.capabilities.has(capability)) {
    return conv.close(...responses.sayLocation(conv.user.storage.location))
  }
  conv.ask(new NewSurface({
    context: responses.newSurfaceContext,
    notification: responses.notificationText,
    capabilities: capability,
  }))
}

如果你的 Node.js 版本支持 async await(我认为默认从 Node.js 7.6+ 开始,但为了安全起见使用 Node.js 8+),那么你可以简化代码不链接 .then:

app.intent('handle_permission', async (conv, params, permissionGranted) => {
  // ...
    const city = await coordinatesToCity(coordinates.latitude, coordinates.longitude)
    conv.user.storage.location = city
    return showLocationOnScreen(conv)
  // ...
}

请注意,Firebase Functions 当前使用 Node.js 6,而 目前不支持 异步等待,因此您需要一个转译器才能在那里使用它。