JavaScript异步API调用问题:speechText未定义及代码重构咨询
没错,你猜的完全正确!这就是异步操作的时序问题——你的代码确实在API请求返回结果之前,就执行了return handlerInput.responseBuilder...这行语句,导致speechText还没被赋值就被使用,自然会报未定义的错误。
问题根源
request.get是基于回调函数的异步HTTP请求:当JavaScript执行到这行代码时,会把请求发送出去,然后立刻继续执行后面的return语句,不会等待API返回结果。只有当API响应回来后,才会调用你传入的回调函数去处理body并赋值给speechText,但这时候响应已经返回给Alexa了,根本没用到这个值。
重构方案:用Async/Await处理异步逻辑
要解决这个问题,我们需要让代码等待API返回结果后再构建响应。这里有两种常见的实现方式:
方式1:封装request为Promise
原生request库本身不支持Promise,但我们可以手动封装它,再用async/await等待结果:
const NumberIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'NumberIntent'; }, // 将handle标记为async函数,自动返回Promise async handle(handlerInput) { console.log('At NumberIntentHandler'); let slotNum = handlerInput.requestEnvelope.request.intent.slots.number.value; const url = `http://numbersapi.com/${slotNum}`; console.log('url: ', url); // 封装request.get为Promise const getNumberFact = () => { return new Promise((resolve, reject) => { request.get(url, (error, response, body) => { if (error) { console.log('error: ', error); reject(error); } else { console.log('statusCode: ', response && response.statusCode); console.log('body: ', body); resolve(body); } }); }); }; try { // 等待API返回结果后再赋值 const speechText = await getNumberFact(); console.log('speechText: ', speechText); // 拿到结果后再构建响应 return handlerInput.responseBuilder .speak(speechText) .withSimpleCard('Here is your fact: ', speechText) .getResponse(); } catch (error) { // 处理请求失败的异常情况 const errorText = 'Sorry, I couldn\'t get a fact for that number right now.'; return handlerInput.responseBuilder .speak(errorText) .withSimpleCard('Error', errorText) .getResponse(); } } };
方式2:使用支持Promise的HTTP库(比如axios)
如果可以更换依赖,用axios这类原生支持Promise的库会更简洁:
首先安装依赖:
npm install axios
然后修改代码:
const axios = require('axios'); const NumberIntentHandler = { canHandle(handlerInput) { return handlerInput.requestEnvelope.request.type === 'IntentRequest' && handlerInput.requestEnvelope.request.intent.name === 'NumberIntent'; }, async handle(handlerInput) { console.log('At NumberIntentHandler'); let slotNum = handlerInput.requestEnvelope.request.intent.slots.number.value; const url = `http://numbersapi.com/${slotNum}`; console.log('url: ', url); try { // 直接用await等待axios请求结果 const response = await axios.get(url); const speechText = response.data; console.log('body: ', speechText); console.log('speechText: ', speechText); return handlerInput.responseBuilder .speak(speechText) .withSimpleCard('Here is your fact: ', speechText) .getResponse(); } catch (error) { console.log('error: ', error); const errorText = 'Sorry, I couldn\'t get a fact for that number right now.'; return handlerInput.responseBuilder .speak(errorText) .withSimpleCard('Error', errorText) .getResponse(); } } };
关键说明
- 把
handle方法标记为async,它会自动返回Promise,Alexa的Skill框架完全支持异步handler。 - 使用
await暂停代码执行,直到异步请求完成,确保speechText有值后再构建响应。 - 添加
try/catch块处理请求失败的情况,避免Skill因API错误崩溃。
内容的提问来源于stack exchange,提问作者Hackbrew
相关产品推荐
相关产品推荐

