Дочерние таблицы NodeJS не определены

Я работаю над ботом Discord/Roblox. Я почти закончил, но столкнулся с проблемой. Я новичок в узле, поэтому, пожалуйста, не ругайтесь слишком сильно, если код плохой. Что это делает, так это то, что я нажимаю кнопку в игре roblox, она отправляет запрос POST в приложение heroku, а затем с предоставленной информацией отправляет сообщение на канал на моем сервере разногласий.

Соответствующие биты кода:

const Discord = require('discord.js');
const request = require('request');
var express = require('express');
    app = express();
    bodyParser = require('body-parser');
    path = require('path');

var bodyParser = require('body-parser');
var PORT = process.env.PORT || 5000;

const bot = new Discord.Client();

function GetData(id){
    let url = "https://economy.roblox.com/v1/assets/"+id+"/resale-data";
    let url2 = "https://thumbnails.roblox.com/v1/assets?assetIds="+id+"&size=150x150&format=Png";
    let url3 = "http://api.roblox.com/marketplace/productinfo?assetId="+id

    let options = {json: true};

    var response = {};

    request(url, options, (error, res, body) => {
        if (error) {
            return console.log(error)
        };

        if (!error && res.statusCode == 200) {
            response.rap = body.recentAveragePrice
            console.log(response)
        };
    });

    request(url2, options, (error, res, body) => {
        if (error) {
            return console.log(error)
        };

        if(!error && res.statusCode == 200) {
            response.thumbnail = body.data[0].imageUrl
            console.log(response)
        }
    });

    request(url3, options, (error, res, body) => {
        if (error) {
            return console.log(error)
        };

        if(!error && res.statusCode == 200) {
            response.name = body.Name
            console.log(response)
        }
    });

    return response
};

app.post('/exec', function(req, res) {
    res.setHeader('Content-Type', 'application/json')
    res.send(JSON.stringify({
        success: true
    }));
    console.log("working");
    console.log(req.body)
    var data = GetData(req.body.id)
    if (req.body.state === true){
        var state = "New Projected"
    } else {
        var state = "No longer projected"
    }
    const msg = new Discord.RichEmbed()
        .setDescription(data.name)
        .setAuthor(state)
        .setColor(0x1c90d9)
        .addField("RAP:", data.rap)
        .setImage(data.thumbnail)
    bot.channels.get("655196831835226133").send(msg);
});

Однако это сообщение, которое отправляется на сервер разногласий:

все не определено и нет эскиза

введите здесь описание изображения

Помощь приветствуется. Большое спасибо.


person Mist    schedule 19.12.2019    source источник
comment
Каков вывод внизу, где вы делаете console.log(req.body);? А также что получится, если вы зарегистрируете объект var data в консоли до создания msg ?   -  person chrisbyte    schedule 19.12.2019
comment
@chrisbyte { id: '6789', state: true }, это то, что я отправил из Roblox. Это не часть моей проблемы. Кроме того, вывод журнала data представляет собой пустую таблицу? Хм....   -  person Mist    schedule 19.12.2019
comment
@chrisbyte Но журналы самой функции внутри requests возвращают действительную таблицу: {name: "Figure1", thumbnail: "long link thing"}   -  person Mist    schedule 19.12.2019
comment
Исходя из этого, я подозреваю, что вызовы request внутри GetData являются асинхронными, и данные еще не доступны к тому времени, когда вы делаете return response. Вы знакомы с Promise или async/await в javascript? github.com/request/request#promises--asyncawait Вам нужно подождать фактические данные, которые нужно вернуть из запроса до, вы можете перейти к следующему запросу и затем, наконец, вернуться.   -  person chrisbyte    schedule 19.12.2019
comment
@chrisbyte Нет, я не такой. Не могли бы вы немного просветить меня в этом, пожалуйста?   -  person Mist    schedule 19.12.2019


Ответы (1)


Немного чтения здесь: https://www.promisejs.org/

Асинхронные функции, такие как выполнение запроса к серверу, требуют времени для возврата и делают это в обратном вызове. Вы должны отслеживать пути обратных вызовов, чтобы убедиться, что вы не выполняете следующий шаг до завершения предыдущего шага, иначе вы полностью пропустите его. Это не похоже на вызов обычной функции, которая делает что-то по порядку.

Когда вы делаете return response в конце GetData, в объекте ничего нет, потому что вам нужно дождаться завершения трех ответов в соответствующих обратных вызовах. Вы видите события, зарегистрированные в консоли, потому что они происходят постфактум в фоновом режиме. Если бы вы поместили метки времени и дополнительные журналы, вы бы увидели, что все это происходит не в том порядке, в котором вы это написали, а скорее в порядке, когда сами запросы завершаются.

Есть много разных способов написания и использования промисов, но очень простым здесь будет вложение запросов, чтобы они выполнялись один за другим, а затем, наконец, resolve обещание в самом конце, когда ваш response объект имеет все данные. С промисами вы resolve то же самое, что и возврат, и reject когда вы хотите вызвать ошибку.

Не вдаваясь слишком глубоко, вот пример того, что вы можете сделать. Это не проверено, так как я не запускаю ваше приложение и не пытаюсь его переписать, для этого не предназначен StackOverflow, но, надеюсь, он может направить вас в правильном направлении. Обратите внимание, что я вызываю reject для ошибок, чтобы промис перестал выполняться.

function GetData(id){

  // declare the Promise
  return new Promise((resolve, reject) => {
    let url = "https://economy.roblox.com/v1/assets/"+id+"/resale-data";
    let url2 = "https://thumbnails.roblox.com/v1/assets?assetIds="+id+"&size=150x150&format=Png";
    let url3 = "http://api.roblox.com/marketplace/productinfo?assetId="+id

    let options = {json: true};

    var response = {};

    // make request 1
    request(url, options, (error1, res1, body1) => {
        if (error1) {
            console.log(error1)

            // reject
            reject(error1);
        };

        if (!error1 && res1.statusCode == 200) {

            // all good, continue

            response.rap = body1.recentAveragePrice
            console.log(response)

            // make request 2
            request(url2, options, (error2, res2, body2) => {
              if (error2) {
                console.log(error2)

                // reject
                reject(error2);
              };

              if(!error2 && res2.statusCode == 200) {

                  // all good, continue

                  response.thumbnail = body2.data[0].imageUrl
                  console.log(response)

                  // make request 3
                  request(url3, options, (error3, res3, body3) => {
                    if (error3) {
                      console.log(error3)

                      // reject
                      reject(error3);
                    };

                    if(!error3 && res3.statusCode == 200) {
                        response.name = body3.Name
                        console.log(response)

                        // finally, resolve the final response object
                        resolve(response);
                    }
                });
              }
          });
        };
    });
  });
};

Теперь к вызывающей функции. Вы по-прежнему вызываете GetData(), но используете функцию then(), чтобы получить то, что было разрешено, или catch(), чтобы отловить любые ошибки. Я бы также предложил поместить ваши функции res.send() в конец, если только вам не нужно сразу возвращаться, как вы изначально написали.

app.post('/exec', function(req, res) {
    res.setHeader('Content-Type', 'application/json')

    console.log("working");
    console.log(req.body)

    // get all the data as a Promise
    GetData(req.body.id)
      .then((data) => {

        // now you have your data!

        if (req.body.state === true){
            var state = "New Projected"
        } else {
            var state = "No longer projected"
        }
        const msg = new Discord.RichEmbed()
            .setDescription(data.name)
            .setAuthor(state)
            .setColor(0x1c90d9)
            .addField("RAP:", data.rap)
            .setImage(data.thumbnail)
        bot.channels.get("655196831835226133").send(msg);

        res.send(JSON.stringify({
            success: true
        }));
      })
      .catch((err) => {

        // you can handle errors here that were rejected in the promise, if necessary

        res.send(JSON.stringify({
            success: false
        }));

      })
});
person chrisbyte    schedule 19.12.2019
comment
Спасибо, но я все еще получаю практически ту же ошибку... TypeError: Cannot read property 'Name' of undefined. Кроме того, я уверен, что он существует, поскольку я видел его в выводе раньше. - person Mist; 21.12.2019