Как устранить ошибку «NaN», когда единственной видимой проблемой является вызов функции и назначение функции let - PullRequest
0 голосов
/ 13 декабря 2018

Я тестирую навык Alexa локально и получаю ошибку, которая просто говорит NaN.Я выяснил, что строка let recipe = getRecipe() - это проблема с помощью операторов console.log().Похоже, что она отсутствует в самой функции getRecipe(), потому что оператор console.log() в самом начале блока try в этой функции не запускается, а в начале перехвата.Заранее спасибо за любые предложения.

Обработчик:

    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' } }

ОБНОВЛЕНИЕ:

@ Shilly.Так что я все еще в замешательстве ... Кроме того, мне пришлось немного отредактировать вашу функцию, чтобы сделать код внутри улова доступным ... но в любом случае я думаю, что то, что я сделал, все еще сохраняет основную логику, которую вы пытались передать.Моя проблема заключается в том, что при разборе я получаю сообщение об ошибке 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

Ответы [ 2 ]

0 голосов
/ 13 декабря 2018

Наконец-то разобрался.Большое спасибо @Shilly за руководство в правильном направлении.Мое понимание асинхронности и ожидания было неверным.Эти источники были полезны:

Возвращение обработчика. Метод ResponseBuilder из метода обещания .then ()

https://medium.com/@tkssharma/writing-neat-asynchronous-node-js-code-with-promises-async-await-fa8d8b0bcd7c

Вот мой обновленный код:

Асинхронный обработчик основан на функции, которую я создал, чтобы использовать Promises с помощью @ Shilly.Вероятно, это не самый краткий способ, но он работает!

Обработчик:

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' } }
0 голосов
/ 13 декабря 2018

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% правильность, не имея возможности самостоятельно протестировать код.

...