Node.js 在我自己的函数中请求模块的问题

Problem with Node.js Request module inside my own function

我有这个代码:

const request = require('request');

let url = "https://api.warframe.market/v1/items";

let options = {json: true};
let item_urls;
//const response;
request(url, options, (error, res, body) => {
    if (error) {
        return  console.log(error)
    };

    if (!error && res.statusCode == 200) {
        console.log(body);
        item_urls = body.payload.items;

    };
});

它工作正常并且 returns JSON 到变量 item_urls

然后我尝试像这样围绕它包装我自己的函数:

const request = require('request');

let url = "https://api.warframe.market/v1/items";

let options = {json: true};
let item_urls;
//const response;

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

    if (!error && res.statusCode == 200) {
        console.log(body);
        item_urls= body.payload.items;

    };
});
};
getitems()

但它不起作用,item_urls最终未定义。除了被包装在我的函数中之外,代码是相同的。如果您想知道为什么我要将它包装在一个函数中——我需要向它传递一个参数,但为了简单起见并为了演示这个问题,我删除了这个逻辑。

谢谢,

编辑:对不起,我忘了提及其他细节。

所以我 运行 直接在我的 IDE 中逐行编译此代码,并将这些行发送到节点 REPL,以便我可以逐行测试代码。如果我们看第一段代码:

我全部突出显示,运行。由于请求是异步的,我先等待它完成,我知道它已完成,因为回调函数将正文输出到控制台,所以我一直等到看到大量数据打印到控制台,这就是我验证命令成功完成。在将正文输出到控制台的代码行之后,它会将正文转储到我的变量中。为了确认这是否有效,我然后在 REPL 终端本身键入 item_urls 并可以看到输出。

对于第二个代码块,我打开了一个新的 REPL,所以一切都很干净。然后我再次执行所有代码行并等待......我期望请求异步调用需要几秒钟才能像第一个代码块一样完成。但是,没有任何内容输出到控制台。我等了 30 秒(之前的代码块只用了大约 5 秒),然后我再次在 REPL 终端输入 item_urls,它显示未定义。

我确信它与异步和回调性质有关,但我不知道是什么。

您必须在您首先获取其值的回调中使用 item_urls。您可以使用该值的唯一方法是在回调本身或您从那里调用的函数中使用它并将值传递给它。

问题是 request() 是异步和非阻塞的。这意味着您执行 request(),它开始操作,然后它 returns 并继续执行更多代码(包括您尝试使用 item_urls 的代码)。但是,此时回调尚未被调用,所以它仍然是 undefined.

最后,您知道 item_urls 唯一有价值的地方是在该回调中。因此,任何使用该值的代码都需要进入该回调内部或从该回调内部调用。这就是异步编程在 node.js.

中的工作方式

所以,它需要看起来像这样:

function getitems(){

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

        if (!error && res.statusCode == 200) {
            console.log(body);
            // use body.payload.items directly here
            // or call some function here and pass it the value
            // Do not assign it to some higher scoped variable and
            // expect to use it elsewhere.  Can't do that.
        }
    });
}

getitems()

如果您想在 getitems 之外获取值,那么您需要使用回调、事件或承诺来在 getitems() 之外传递异步检索的值。您可以在这个非常受欢迎的答案中看到为什么不能只 return 来自 getitems() 的值的完整讨论:How do I return the response from an asynchronous call.


我不知道您认为代码在函数中的第二个代码块不起作用。我只是把它自己放到一个文件中并执行它,它工作得很好。因此,如果您从等式中删除 item_urls 并像我上面建议的那样在回调中使用结果,即使在第二个代码块中,它也能正常工作。

这是我刚 运行:

的确切程序
const request = require('request');

let url = "https://api.warframe.market/v1/items";

let options = {
    json: true
};
function getitems() {
    request(url, options, (error, res, body) => {
        if (error) {
            return console.log(error);
        }

        if (res.statusCode === 200) {
            console.log(body.payload.items);
        }
    });
}
getitems()

此代码有效。它给我这样的输出:

[
  {
    id: '5835a4564b0377e226bdc360',
    url_name: 'axi_c1_intact',
    thumb: 'icons/en/thumbs/Axi_C1_Intact.99413ebefc5b87d57d9e5314265b56de.128x128.png',
    item_name: 'Axi C1 Intact'
  },
  {
    id: '5835a4dd4b0377e226bdc380',
    url_name: 'meso_s3_intact',
    thumb: 'icons/en/thumbs/Meso_S3_Intact.caee59471a7b06ca040f2d257083e008.128x128.png',
    item_name: 'Meso S3 Intact'
  },
  {
    id: '58d8f31c11efe42a5e523215',
    url_name: 'lith_n2_exceptional',
    thumb: 'icons/en/thumbs/Lith_N2_Exceptional.b82a140ba17908be7226fddcecd7bf62.128x128.png',
    item_name: 'Lith N2 Exceptional'
  },
  {
    id: '5936dd6916efa3a742b15f48',
    url_name: 'lith_n3_exceptional',
    thumb: 'icons/en/thumbs/Lith_N3_Exceptional.b82a140ba17908be7226fddcecd7bf62.128x128.png',
    item_name: 'Lith N3 Exceptional'
  },
  {
    id: '5835a4404b0377e226bdc358',
    url_name: 'neo_v4_intact',
    thumb: 'icons/en/thumbs/Neo_V4_Intact.e0e38afae723757487927845b35a81d2.128x128.png',
    item_name: 'Neo V4 Intact'
  },

  .... a whole bunch more items