【问题标题】:Nodejs global variable scope issueNodejs全局变量范围问题
【发布时间】:2020-02-29 19:01:45
【问题描述】:

我对 Nodejs 很陌生。在以下代码中,我从 API 获取 json 数据。

let data_json = ''; // global variable

app.get('/', (req, res) => {
    request('http://my-api.com/data-export.json', (error, response, body) => {
        data_json = JSON.parse(body);
        console.log( data_json ); // data prints successfully
    });

    console.log(data_json, 'Data Test - outside request code'); // no data is printed
})

data_json 是我的全局变量,我分配了请求函数返回的数据。在该函数中,json 数据打印得很好。但我尝试在请求函数之外打印相同的数据,但没有打印出来。

我犯了什么错误?

【问题讨论】:

  • console.log 出现在您的请求之前,查看获取异步数据的方法:回调、承诺或异步等待

标签: node.js variables scope global-variables


【解决方案1】:

Node.js 将在外部执行代码,而不是等待请求解析(从您的 API 获取数据),它不会打印任何内容,因为在执行时仍然没有任何内容,并且只有在节点从您的 api(这将需要几毫秒)将执行请求中的代码。这是因为 nodejs 是异步和非阻塞语言,这意味着它不会阻塞或停止代码,直到您的 api 返回数据,它会继续运行并在收到响应后完成。

在回调函数中完成所有你想要的数据操作是一个很好的做法,不幸的是你不能依赖你拥有的结构。

这是您的代码示例,只是注释掉了操作顺序:

let data_json = ''; // global variable

app.get('/', (req, res) => {
    //NodeJS STARTS executing this code
    request('http://my-api.com/data-export.json', (error, response, body) => {
        //NodeJS executes this code last, after the data is loaded from the server
        data_json = JSON.parse(body);
        console.log( data_json );
        //You should do all of your data_json manipluation here
        //Eg saving stuff to the database, processing data, just usual logic ya know
    });

    //NodeJS executes this code 2nd, before your server responds with data
    //Because it doesn't want to block the entire code until it gets a response
    console.log(data_json, 'Data Test - outside request code'); 
})

假设您想使用第一个请求中的数据发出另一个请求 - 您必须执行以下操作:

request('https://your-api.com/export-data.json', (err, res, body) => {
    request('https://your-api.com/2nd-endpoint.json', (err, res, body) => {
         //Process data and repeat
     })
    })

如您所见,这种模式很快就会变得非常混乱——这被称为回调地狱,所以为了避免大量嵌套请求,有一种语法糖可以使这段代码看起来更加花哨和可维护,它被称为异步/等待模式。以下是它的工作原理:

let data_json = ''

app.get('/', async (req,res) => {
    try{
        let response = await request('https://your-api.com/endpoint')
        data_json = response.body
    } catch(error) {
        //Handle error how you see fit
    }
    console.log(data_json) //It will work
})

此代码与您拥有的代码执行相同的操作,但不同之处在于您可以一个接一个地创建任意数量的await request(...),并且没有嵌套。 唯一的区别是你必须声明你的函数是异步的 async (req, res) => {...} 并且所有的 let var = await request(...) 都需要嵌套在 try-catch 块中。这样你就可以发现你的错误。如果您认为有必要,您可以将所有请求放在 catch 块中。

希望这会有所帮助:)

【讨论】:

  • 感谢 Stefan 的详尽而翔实的回复。
  • @sisko 很高兴我能帮上忙,祝你的 nodejs 之旅好运,一旦你了解它,它就是一门很棒的语言 :)
【解决方案2】:

console.log 出现在您的请求之前,查看获取异步数据的方法:回调、promise 或 async-await。 Nodejs API 是异步的(大部分),因此外部 console.log 将在 request API 调用完成之前执行。

let data_json = ''; // global variable
app.get('/', (req, res) => {
    let pr = new Promise(function(resolve, reject) {

        request('http://my-api.com/data-export.json', (error, response, body) => {
            if (error) {
                reject(error)
            } else {
                data_json = JSON.parse(body);
                console.log(data_json); // data prints successfully
                resolve(data_json)
            }
        });

    })
    pr.then(function(data) {
      // data also will have data_json
      // handle response here

     console.log(data_json); // data prints successfully

    }).catch(function(err) {
        // handle error here
    })

})

如果您不想创建 Promise 包装器,可以使用 Request 模块团队创建的 request-promise-native(使用原生 Promises)。

了解callbackspromises,当然还有async-await

【讨论】:

  • 感谢您的回答。我现在明白我做错了什么。非常感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-17
  • 1970-01-01
  • 2010-11-26
  • 2012-04-29
  • 2020-11-15
  • 2017-02-17
相关资源
最近更新 更多