【问题标题】:Amazon Alexa Skill Callback not being called未调用 Amazon Alexa 技能回调
【发布时间】:2017-11-09 22:32:08
【问题描述】:

我有一项 Alexa 技能,可以获取设备的地址并使用该信息来查找附近的设施。

我查看了@jfriend00 提出的解决方案here。我的回调结构相同。我仍然不明白为什么在其余代码运行之前我的回调没有返回。我应该会看到来自getJSON() 回调的console.info() 调用,但它永远不会运行。

编辑:我显然不明白这个异步的东西是如何工作的,因为我的日志完全不合时宜。有人可以向我解释发生了什么吗?我查看了我链接的解决方案,我的代码看起来与他的准系统解决方案尽可能相似。我不明白我哪里出错了。

控制台日志

getKaiserBuildingHandler()

const getKaiserBuildingHandler = function() {
    console.info("Starting getKaiserBuildingHandler()");
    
    ...

      switch(addressResponse.statusCode) {
          case 200:
              console.log("Address successfully retrieved, now responding to user.");
              const addressObj = addressResponse.address;
              const address = `${addressObj['addressLine1']}, ${addressObj['city']}, ${addressObj['stateOrRegion']} ${addressObj['postalCode']}`;
              var ADDRESS_MESSAGE = Messages.ADDRESS_AVAILABLE;
              const alexa = this;

              getKaiserBuildingHelper(buildingType, address, function(response) {
                ADDRESS_MESSAGE += response;
                alexa.emit(":tell", ADDRESS_MESSAGE);
                console.info("Ending getKaiserBuildingHandler()");
              });

              break;
          
          ...
      }
    });

    ...
};

getKaiserBuildingHelper()

const getKaiserBuildingHelper = function(buildingType, address, callback) {
  console.info("Starting getKaiserBuildingHelper()");

  // var facilityOutput = Messages.ERROR;
  var facilityOutput = "Inside building helper function, initial value.";

  if (buildingType == BLDG_TYPE.PHARMACY ||
      buildingType == BLDG_TYPE.CLINIC ||
      buildingType == BLDG_TYPE.HOSPITAL) {

    ...
    
    facilityOutput = "Before get JSON call.";

    getJSON(buildingType, function(err, data) {
      if (data != "ERROR") {
        console.info("Received data from callback: ", data);
        facilityOutput = "The closest Kaiser Permanente " + buildingType + " to your location is located at " + data + ".";
      } else {
        console.error("Error with data received from callback. ", err);
        facilityOutput = "Entered JSON call, returned ERROR.";
      }
    });
  }

  ...

  callback(facilityOutput);
  console.info("Ending getKaiserBuildingHelper()");
}

getJSON()

const getJSON = function(building, callback) {
  console.info("Starting getJSON()");

  Axios
    .get(getFacilitySearchEndpoint(building))
    .then(function(response) {
      const responseData = response.data.query.search[0].title;
      callback(null, responseData);
      console.info("Data retrieved from Axios call");
      console.info("Ending getJSON()");
    })
    .catch(function(error) {
      callback(error, "ERROR");
      console.info("Error caught from Axios call");
      console.info("Ending getJSON()");
    });
}

getFacilitySearchEndpoint() [wikipedia api just as placeholder]

const getFacilitySearchEndpoint = function(building) {
  console.info("Starting getFacilitySearchEndpoint()");

  switch (building) {
    case BLDG_TYPE.HOSPITAL:
      console.info("Ending getFacilitySearchEndpoint() with " + building);
      return "https://en.wikipedia.org/w/api.php?action=query&format=json&list=search&utf8=1&srsearch=Albert+Einstein";
      break;
    case BLDG_TYPE.PHARMACY:
      console.info("Ending getFacilitySearchEndpoint() with " + building);
      return "https://en.wikipedia.org/w/api.php?action=query&format=json&list=search&utf8=1&srsearch=Harry+Potter";
      break;
    case BLDG_TYPE.CLINIC:
      console.info("Ending getFacilitySearchEndpoint() with " + building);
      return "https://en.wikipedia.org/w/api.php?action=query&format=json&list=search&utf8=1&srsearch=Tony+Stark";
      break;
    default:
      console.info("Ending getFacilitySearchEndpoint() with default");
      break;
  }
  console.info("Ending getFacilitySearchEndpoint()");
}

【问题讨论】:

  • 当 Promise Axios Promise 解决后,尝试在 callback(null, response) 之后立即移动它
  • 虽然理论上它应该按原样触发,但由于 async/promises 的工作方式,顺序错误
  • @djfdev,我刚刚对代码进行了编辑,虽然不是你的建议。我在正确的轨道上吗?
  • 我认为您误解了异步的工作原理。在您提供的链接中查看 Felix King 的答案。 TLDR 异步函数(如 Axios 请求和您的 getJSON 包装器)立即返回,因此看起来您添加的新回调将出现故障。
  • 我明白你在说什么。根据日志,代码返回乱序。我对异步有什么误解?

标签: javascript node.js httprequest axios


【解决方案1】:

我在聊天中留下了一些笔记,但这里的情况可能更清楚。一定要在您提供的链接中再看看 Felix 的答案,但是由于您在这里有很多代码,如果我在这种情况下解释会更容易理解。

Axios.get 是异步的,这意味着它实际上 HTTP 请求完成之前返回。因此,getJSON 正文中在您发出请求后发生的任何事情,实际上都会在传递给 thencatch 的函数被调用之前发生:

function getJSON (url, callback) {
  Axios
    .get(url)
    .then(function (data) {
      console.log('I happen later, when the request is finished')
      callback(null, data)
    })
    .catch(function (err) {
      console.log('I happen later, only if there was an error')
      callback(err)
    })

  console.log('I happen immediately!')
}

很容易混淆——在异步代码中,事情不一定按照它们在代码中出现的顺序发生。所以我们在调用异步函数的时候要注意这一点。

这意味着getKaiserBuildingHelper 函数内部存在问题。因为您的 getJSON 函数只是 Axios 承诺的回调样式包装器,所以它也是异步的。它将在请求完成之前立即返回,并且函数体内的其余代码将继续正常运行。

function getKaiserBuildingHelper (callback) {
  let facilityOutput = 'initial value'

  getJSON('http://some-url.com', function (err, data) {
    console.log('I happen later, when the request is complete')

    if (err) {
      facilityOutput = 'Error!'
    } else {
      facilityOutput = 'Success!'
    }
  })

  console.log('I happen immediately!')
  callback(facilityOutput)
}

这意味着最后一行 callback(facilityOutput) 发生在 getJSON 回调之前。因此,facilityOutput 的值保持不变。但是您可以通过将回调移动到 getJSON 回调中来轻松解决此问题:

function getKaiserBuildingHelper (callback) {
  getJSON('http://some-url.com', function (err, data) {
    if (err) {
      callback('Error!')
    } else {
      callback('Success!')
    }
  })
}

现在您可以按预期使用该功能了:

getKaiserBuildingHelper(function (message) {
  console.log(message) // => 'Success!' or 'Error!'
}

最后,我不确定您为什么要在该回调中添加this.emit。那是从别处复制粘贴的东西吗?

【讨论】:

  • 非常感谢@djfdev,相信我现在对异步回调有了更好的理解。本质上,我的问题是我误解了异步回调首先运行的代码。为了回答您的最后一个问题,this.emit() 调用已添加到 getKaiserBuildingHandler() 中的回调中,因为我希望它在 返回 response 并添加到 ADDRESS_MESSAGE 之后运行。
  • 但是,当this.emit() 在回调中时,this 不再在应用程序的全局范围内。但是this.emit()不会在回调函数在getKaiserBuildingHandler()运行之前运行吗?
  • 为了将emit() 调用保留在回调中,因为它在获得必要信息之前不会触发,在回调之前创建了一个const alexa = this;,然后在回调中调用alexa.emit()。这是好的做法还是太“hacky”了?
  • 我有点迷茫,没有看到所有的代码 TBH ......但如果它有效的话。您可以尝试使用箭头函数或Function.prototype.call 来建立特定的上下文。
  • 如果您有时间,我已将回复移至chat
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-03
  • 2017-02-27
  • 1970-01-01
  • 2017-08-03
  • 1970-01-01
  • 2018-10-01
相关资源
最近更新 更多