当调用函数并分配给 let 是唯一可见的问题时,如何解决 "NaN" 错误
How to trouble shoot "NaN" error when the call to a function and assigning to let is the only visible issue
我正在本地测试 Alexa 技能并收到一个仅显示 NaN 的错误。我已经通过 console.log()
语句发现第 let recipe = getRecipe()
行是问题所在。它似乎不在 getRecipe()
函数本身中,因为该函数中 try 块最开头的 console.log()
语句不是 运行,而是开头的语句的捕获量确实如此。在此先感谢您的任何建议。
处理程序:
handle(handlerInput){
const attributes = handlerInput.attributesManager.getSessionAttributes();
const request = handlerInput.requestEnvelope.request;
switch (attributes.previousIntent){
case "FoodIntent":
if(request.intent.slots.answer.resolutions.resolutionsPerAuthority[0].values[0].value.name === 'yes'){
let randomFood = Helpers.suggestFood(handlerInput);
let queryFood = randomFood.replace(/\s+/g, '-').toLowerCase(); event
attributes.currentSuggestedFood = queryFood;
const speechText = 'Great! In the future I will be able to look up the ingredients for you.'
console.log('before call getRecipe()')
let recipe = getRecipe(handlerInput)
console.log('After call getRecipe()')
return handlerInput.responseBuilder
.speak(speechText + " "+ recipe)
.reprompt(speechText)
.withShouldEndSession(true)
.withSimpleCard('Cheer Up - YesNo', speechText)
.getResponse();
} else {
let randomFood = Helpers.suggestFood(handlerInput);
let speechText = ResponseToUsersNo[Math.floor(Math.random() * ResponseToUsersNo.length)]+
FoodPrefixes[Math.floor(Math.random() * FoodPrefixes.length)] +
randomFood + FoodSuffixes[Math.floor(Math.random() * FoodSuffixes.length)];
let repromptText = 'Did the last suggestion work for you?'
handlerInput.attributesManager.setSessionAttributes(attributes);
if (attributes.FoodsAlreadySuggested.length >= 10) {
speechText = 'I feel like you don\'t actually want anything. So I\'m leaving for now, talk to you later.'
return handlerInput.responseBuilder
.speak(speechText)
.withShouldEndSession(true)
.withSimpleCard('Cheer Up - YesNo', speechText)
.getResponse();
}
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(repromptText)
.withSimpleCard('Cheer Up - YesNo', speechText)
.getResponse();
}
case "HobbyIntent":
if(request.intent.slots
和 getRecipe() 函数:
async function getRecipe(handlerInput) {
try{
console.log('before attributes')
const attributes = handlerInput.attributesManager.getSessionAttributes();
console.log('attributes: '+ attributes)
console.log('before url')
const url = `https://api.edamam.com/search?q=${attributes.currentSuggestedFood}&app_id=${FOOD_APP_ID}&app_key=${FOOD_APP_KEY}`; //&from=0&to=3&calories=591-722&health=alcohol-free this was on the end of the uri
console.log(url)
console.log('after url')
request.get(url, (error, response, body) => {
// let json = JSON.parse(body);
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the body
//const theRecipe = await body;
const payload = JSON.parse(body)
console.log("The ingredients for "+ payload.q + " is: ")
console.log(payload.hits[0].recipe.ingredientLines)
return (payload.hits[0].recipe.ingredientLines);
});
}
catch(err){
console.log('before error statement in catch')
console.error('There was an error: ', + err)
}
};
这是我的输出:
before call getRecipe()
before attributes
attributes: [object Object]
before url
https://api.edamam.com/search?q=rellenos-de-papa&app_id=b4dbea92&app_key=8d916c99b930b77c8cbb4615f0800df7
after url
before error statement in catch
There was an error: NaN
After call getRecipe()
{ version: '1.0',
response:
{ outputSpeech:
{ type: 'SSML',
ssml: '<speak>Great! In the future I will be able to look up the ingredients for you. The ingredients are [object Promise]</speak>' },
reprompt: { outputSpeech: [Object] },
shouldEndSession: true,
card:
{ type: 'Simple',
title: 'Cheer Up - YesNo',
content: 'Great! In the future I will be able to look up the
ingredients for you.' } },
userAgent: 'ask-node/2.3.0 Node/v8.12.0',
sessionAttributes:
{ foodType: 'PuertoRican',
FoodsAlreadySuggested: [ 'Platanos Maduros', 'Rellenos de Papa' ],
previousIntent: 'FoodIntent',
state: '_YES_NO',
currentSuggestedFood: 'rellenos-de-papa' } }
更新:
@雪莉。所以我仍然很困惑......顺便说一句,我不得不稍微编辑你的函数以使捕获中的代码可以访问......但无论如何我认为我所做的仍然保留了你试图传授的核心逻辑。
我的问题是在解析 unexpected token o in JSON at position 1
时出现错误。我认为这通常意味着我不需要解析它,因为它已经是一个有效的 js 对象。凉爽的。所以我删除了解析,但随后我得到 Cannot read property '0' of undefined.
,当然指的是我的 return payload.hits[0].recipe.ingredientLines
。似乎无法理解为什么。非常感谢您的帮助。
function getRecipe(handlerInput) {
const attributes = handlerInput.attributesManager.getSessionAttributes();
const url = `https://api.edamam.com/search?q=${attributes.currentSuggestedFood}&app_id=${FOOD_APP_ID}&app_key=${FOOD_APP_KEY}`; //&from=0&to=3&calories=591-722&health=alcohol-free this was on the end of the uri
return get( url, ( response, body ) => {
const payload = JSON.parse(body)
console.log(payload)
return payload.hits[0].recipe.ingredientLines;
}).catch( error => {
console.error( `failed GET request for: ${ url }` );
console.error( error );
});
};
另外这里是响应正文的开头,我看没有解析...body: '{\n "q" : "tostones",\n "from" : 0,\n "to" : 10,\n "params" : {\n
const attributes = handlerInput.attributesManager.getSessionAttributes()
抛出 NaN 错误,原因不明。
catch() 处理程序捕获此错误并将其记录下来。
但是由于该函数是异步的,并且您没有解析 catch 子句中的承诺,因此您得到的是该承诺的这个 [object Promise]
字符串化版本,而不是实际的成分列表。
编辑:
您是否考虑过使用 util.promisify() 这样您就不必混用回调和承诺?
const { promisify } = require( 'util' );
const request = require( 'request' );
const get = promisify( request.get );
function getRecipe(handlerInput) {
const attributes = handlerInput.attributesManager.getSessionAttributes();
const url = `https://api.edamam.com/search?q=${attributes.currentSuggestedFood}&app_id=${FOOD_APP_ID}&app_key=${FOOD_APP_KEY}`; //&from=0&to=3&calories=591-722&health=alcohol-free this was on the end of the uri
return get( url, ( response, body ) => {
const payload = JSON.parse(body)
return payload.hits[0].recipe.ingredientLines;
}).catch( error ) {
console.error( `failed GET request for: ${ url }` );
console.error( error );
});
};
同样可以用 async/await 风格编写,但我不够流利,无法在无法自己测试代码的情况下使其 100% 正确。
终于想通了。非常感谢@Shilly 指导我正确的方向。我对异步和等待的理解是错误的。这些资源很有帮助:
Returning handler.ResponseBuilder from promise.then() method
这是我更新后的代码:
异步处理程序依赖于我在@Shilly 的帮助下创建的使用 Promises 的函数。这可能不是最简洁的方法,但它确实有效!
处理程序:
async handle(handlerInput){
const attributes = handlerInput.attributesManager.getSessionAttributes();
const request = handlerInput.requestEnvelope.request;
switch (attributes.previousIntent){
case "FoodIntent":
if(request.intent.slots.answer.resolutions.resolutionsPerAuthority[0].values[0].value.name === 'yes'){
let randomFood = Helpers.suggestFood(handlerInput);
let queryFood = randomFood.replace(/\s+/g, '-').toLowerCase();
attributes.currentSuggestedFood = queryFood;
const speechText = 'Great! Here are the ingredients!'
let recipe = await getRecipe(handlerInput)
let recipeIngredients = recipe.hits[0].recipe.ingredientLines;
return handlerInput.responseBuilder
.speak(speechText+ 'The ingredients are '+ recipeIngredients)
.reprompt(speechText)
.withShouldEndSession(true)
.withSimpleCard('Cheer Up - YesIntentFood', recipeIngredients)
.getResponse();
函数:
async function getRecipe(handlerInput) {
const attributes = handlerInput.attributesManager.getSessionAttributes();
const url = `https://api.edamam.com/search?q=${attributes.currentSuggestedFood}&app_id=${FOOD_APP_ID}&app_key=${FOOD_APP_KEY}`;
console.log(url)
return new Promise (function(resolve, reject) {
request.get(url, (error, response, body) => {
if (error) {
reject(error);
} else {
resolve(JSON.parse(body))
}
});
})
};
输出:
https://api.edamam.com/search?q=pernil&app_id=b4dbea92&app_key=8d916c99b930b77c8cbb4615f0800df7
{ version: '1.0',
response:
{ outputSpeech:
{ type: 'SSML',
ssml: '<speak>Great! In the future I will be able to look up the ingredients for you.The ingredients are 2 1/2 pounds pork shoulder, boston butt, pernil,2 garlic cloves,1 small onion,1 bunch cilantro,1 jalapeño,1 cup orange juice,1 cup pineapple juice,1 lemon,Handfuls salt,Pepper to taste,Ground cumin</speak>' },
reprompt: { outputSpeech: [Object] },
shouldEndSession: true,
card:
{ type: 'Simple',
title: 'Cheer Up - YesIntentFood',
content: [Array] } },
userAgent: 'ask-node/2.3.0 Node/v8.12.0',
sessionAttributes:
{ foodType: 'PuertoRican',
FoodsAlreadySuggested: [ 'Platanos Maduros', 'Pernil' ],
previousIntent: 'FoodIntent',
state: '_YES_NO',
currentSuggestedFood: 'pernil' } }
我正在本地测试 Alexa 技能并收到一个仅显示 NaN 的错误。我已经通过 console.log()
语句发现第 let recipe = getRecipe()
行是问题所在。它似乎不在 getRecipe()
函数本身中,因为该函数中 try 块最开头的 console.log()
语句不是 运行,而是开头的语句的捕获量确实如此。在此先感谢您的任何建议。
处理程序:
handle(handlerInput){
const attributes = handlerInput.attributesManager.getSessionAttributes();
const request = handlerInput.requestEnvelope.request;
switch (attributes.previousIntent){
case "FoodIntent":
if(request.intent.slots.answer.resolutions.resolutionsPerAuthority[0].values[0].value.name === 'yes'){
let randomFood = Helpers.suggestFood(handlerInput);
let queryFood = randomFood.replace(/\s+/g, '-').toLowerCase(); event
attributes.currentSuggestedFood = queryFood;
const speechText = 'Great! In the future I will be able to look up the ingredients for you.'
console.log('before call getRecipe()')
let recipe = getRecipe(handlerInput)
console.log('After call getRecipe()')
return handlerInput.responseBuilder
.speak(speechText + " "+ recipe)
.reprompt(speechText)
.withShouldEndSession(true)
.withSimpleCard('Cheer Up - YesNo', speechText)
.getResponse();
} else {
let randomFood = Helpers.suggestFood(handlerInput);
let speechText = ResponseToUsersNo[Math.floor(Math.random() * ResponseToUsersNo.length)]+
FoodPrefixes[Math.floor(Math.random() * FoodPrefixes.length)] +
randomFood + FoodSuffixes[Math.floor(Math.random() * FoodSuffixes.length)];
let repromptText = 'Did the last suggestion work for you?'
handlerInput.attributesManager.setSessionAttributes(attributes);
if (attributes.FoodsAlreadySuggested.length >= 10) {
speechText = 'I feel like you don\'t actually want anything. So I\'m leaving for now, talk to you later.'
return handlerInput.responseBuilder
.speak(speechText)
.withShouldEndSession(true)
.withSimpleCard('Cheer Up - YesNo', speechText)
.getResponse();
}
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(repromptText)
.withSimpleCard('Cheer Up - YesNo', speechText)
.getResponse();
}
case "HobbyIntent":
if(request.intent.slots
和 getRecipe() 函数:
async function getRecipe(handlerInput) {
try{
console.log('before attributes')
const attributes = handlerInput.attributesManager.getSessionAttributes();
console.log('attributes: '+ attributes)
console.log('before url')
const url = `https://api.edamam.com/search?q=${attributes.currentSuggestedFood}&app_id=${FOOD_APP_ID}&app_key=${FOOD_APP_KEY}`; //&from=0&to=3&calories=591-722&health=alcohol-free this was on the end of the uri
console.log(url)
console.log('after url')
request.get(url, (error, response, body) => {
// let json = JSON.parse(body);
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the body
//const theRecipe = await body;
const payload = JSON.parse(body)
console.log("The ingredients for "+ payload.q + " is: ")
console.log(payload.hits[0].recipe.ingredientLines)
return (payload.hits[0].recipe.ingredientLines);
});
}
catch(err){
console.log('before error statement in catch')
console.error('There was an error: ', + err)
}
};
这是我的输出:
before call getRecipe()
before attributes
attributes: [object Object]
before url
https://api.edamam.com/search?q=rellenos-de-papa&app_id=b4dbea92&app_key=8d916c99b930b77c8cbb4615f0800df7
after url
before error statement in catch
There was an error: NaN
After call getRecipe()
{ version: '1.0',
response:
{ outputSpeech:
{ type: 'SSML',
ssml: '<speak>Great! In the future I will be able to look up the ingredients for you. The ingredients are [object Promise]</speak>' },
reprompt: { outputSpeech: [Object] },
shouldEndSession: true,
card:
{ type: 'Simple',
title: 'Cheer Up - YesNo',
content: 'Great! In the future I will be able to look up the
ingredients for you.' } },
userAgent: 'ask-node/2.3.0 Node/v8.12.0',
sessionAttributes:
{ foodType: 'PuertoRican',
FoodsAlreadySuggested: [ 'Platanos Maduros', 'Rellenos de Papa' ],
previousIntent: 'FoodIntent',
state: '_YES_NO',
currentSuggestedFood: 'rellenos-de-papa' } }
更新:
@雪莉。所以我仍然很困惑......顺便说一句,我不得不稍微编辑你的函数以使捕获中的代码可以访问......但无论如何我认为我所做的仍然保留了你试图传授的核心逻辑。
我的问题是在解析 unexpected token o in JSON at position 1
时出现错误。我认为这通常意味着我不需要解析它,因为它已经是一个有效的 js 对象。凉爽的。所以我删除了解析,但随后我得到 Cannot read property '0' of undefined.
,当然指的是我的 return payload.hits[0].recipe.ingredientLines
。似乎无法理解为什么。非常感谢您的帮助。
function getRecipe(handlerInput) {
const attributes = handlerInput.attributesManager.getSessionAttributes();
const url = `https://api.edamam.com/search?q=${attributes.currentSuggestedFood}&app_id=${FOOD_APP_ID}&app_key=${FOOD_APP_KEY}`; //&from=0&to=3&calories=591-722&health=alcohol-free this was on the end of the uri
return get( url, ( response, body ) => {
const payload = JSON.parse(body)
console.log(payload)
return payload.hits[0].recipe.ingredientLines;
}).catch( error => {
console.error( `failed GET request for: ${ url }` );
console.error( error );
});
};
另外这里是响应正文的开头,我看没有解析...body: '{\n "q" : "tostones",\n "from" : 0,\n "to" : 10,\n "params" : {\n
const attributes = handlerInput.attributesManager.getSessionAttributes()
抛出 NaN 错误,原因不明。
catch() 处理程序捕获此错误并将其记录下来。
但是由于该函数是异步的,并且您没有解析 catch 子句中的承诺,因此您得到的是该承诺的这个 [object Promise]
字符串化版本,而不是实际的成分列表。
编辑:
您是否考虑过使用 util.promisify() 这样您就不必混用回调和承诺?
const { promisify } = require( 'util' );
const request = require( 'request' );
const get = promisify( request.get );
function getRecipe(handlerInput) {
const attributes = handlerInput.attributesManager.getSessionAttributes();
const url = `https://api.edamam.com/search?q=${attributes.currentSuggestedFood}&app_id=${FOOD_APP_ID}&app_key=${FOOD_APP_KEY}`; //&from=0&to=3&calories=591-722&health=alcohol-free this was on the end of the uri
return get( url, ( response, body ) => {
const payload = JSON.parse(body)
return payload.hits[0].recipe.ingredientLines;
}).catch( error ) {
console.error( `failed GET request for: ${ url }` );
console.error( error );
});
};
同样可以用 async/await 风格编写,但我不够流利,无法在无法自己测试代码的情况下使其 100% 正确。
终于想通了。非常感谢@Shilly 指导我正确的方向。我对异步和等待的理解是错误的。这些资源很有帮助:
Returning handler.ResponseBuilder from promise.then() method
这是我更新后的代码:
异步处理程序依赖于我在@Shilly 的帮助下创建的使用 Promises 的函数。这可能不是最简洁的方法,但它确实有效!
处理程序:
async handle(handlerInput){
const attributes = handlerInput.attributesManager.getSessionAttributes();
const request = handlerInput.requestEnvelope.request;
switch (attributes.previousIntent){
case "FoodIntent":
if(request.intent.slots.answer.resolutions.resolutionsPerAuthority[0].values[0].value.name === 'yes'){
let randomFood = Helpers.suggestFood(handlerInput);
let queryFood = randomFood.replace(/\s+/g, '-').toLowerCase();
attributes.currentSuggestedFood = queryFood;
const speechText = 'Great! Here are the ingredients!'
let recipe = await getRecipe(handlerInput)
let recipeIngredients = recipe.hits[0].recipe.ingredientLines;
return handlerInput.responseBuilder
.speak(speechText+ 'The ingredients are '+ recipeIngredients)
.reprompt(speechText)
.withShouldEndSession(true)
.withSimpleCard('Cheer Up - YesIntentFood', recipeIngredients)
.getResponse();
函数:
async function getRecipe(handlerInput) {
const attributes = handlerInput.attributesManager.getSessionAttributes();
const url = `https://api.edamam.com/search?q=${attributes.currentSuggestedFood}&app_id=${FOOD_APP_ID}&app_key=${FOOD_APP_KEY}`;
console.log(url)
return new Promise (function(resolve, reject) {
request.get(url, (error, response, body) => {
if (error) {
reject(error);
} else {
resolve(JSON.parse(body))
}
});
})
};
输出:
https://api.edamam.com/search?q=pernil&app_id=b4dbea92&app_key=8d916c99b930b77c8cbb4615f0800df7
{ version: '1.0',
response:
{ outputSpeech:
{ type: 'SSML',
ssml: '<speak>Great! In the future I will be able to look up the ingredients for you.The ingredients are 2 1/2 pounds pork shoulder, boston butt, pernil,2 garlic cloves,1 small onion,1 bunch cilantro,1 jalapeño,1 cup orange juice,1 cup pineapple juice,1 lemon,Handfuls salt,Pepper to taste,Ground cumin</speak>' },
reprompt: { outputSpeech: [Object] },
shouldEndSession: true,
card:
{ type: 'Simple',
title: 'Cheer Up - YesIntentFood',
content: [Array] } },
userAgent: 'ask-node/2.3.0 Node/v8.12.0',
sessionAttributes:
{ foodType: 'PuertoRican',
FoodsAlreadySuggested: [ 'Platanos Maduros', 'Pernil' ],
previousIntent: 'FoodIntent',
state: '_YES_NO',
currentSuggestedFood: 'pernil' } }