使用Javascript与Express时为何设置全局变量后仍输出undefined?
问题根因
你遇到的price始终为undefined的问题,本质来自三个逻辑漏洞:
- 你定义了
getData异步取数函数,但全程没有调用这个函数,代码里没有任何触发取数的逻辑,price从声明后就一直保持未赋值的初始状态,根本不会被修改。 - 同步代码的执行优先级远高于异步操作:哪怕你主动调用了
getData,后面紧跟着的console.log(price)会先执行,此时接口请求还在网络等待阶段,代码还没跑到price = data.c这一行,打印出来的自然是未赋值的初始值。 - 逻辑放置的位置不符合Express的运行流程:你把模板渲染的绑定逻辑放在了服务启动阶段,就算你真的在启动时拉到了一次价格,后续所有请求拿到的都是服务启动那一刻的旧值,甚至如果取数失败,传给模板的就是未定义的空值。
修正方案
根据你的实际业务需求,选对应的实现即可,不需要依赖全局变量存临时值:
方案1:每次访问首页时拉取最新接口数据(推荐)
这是最符合Express路由设计的写法,不会出现脏数据、时序错位的问题:
const express = require("express"); const app = express() const fetch = require("node-fetch"); app.set('view engine', 'ejs'); app.use(express.static('Public')); // 给路由处理函数加async,支持await等待异步请求完成 app.get('/', async function (req, res) { try { const url = "你的带密钥的接口地址"; // 等待接口返回后再继续执行 const response = await fetch(url); const data = await response.json(); const price = data.c; // 拿到有效数据后再传给EJS模板渲染 res.render('index', { price }) } catch (error) { // 捕获接口异常,避免服务因为未处理的Promise rejection崩溃 res.status(500).send("数据加载失败,请稍后重试") } }); app.listen(3000);
方案2:服务启动时拉取一次数据,全局复用
如果你的业务场景是只需要服务启动时拉一次价格,后续所有请求都用这个固定值,那必须等取数完成后再启动服务:
const express = require("express"); const app = express() const fetch = require("node-fetch"); app.set('view engine', 'ejs'); app.use(express.static('Public')); // 用let替代旧的var,避免变量提升带来的不可预期问题 let price; const url = "你的带密钥的接口地址"; async function bootStrap() { // 启动流程第一步:先完成接口取数 const response = await fetch(url); const data = await response.json(); price = data.c; // 取数完成后再启动HTTP服务,此时price已经是有效值 app.listen(3000, () => { console.log("服务启动完成,当前价格:", price) }); } app.get('/', function (req, res) { // 因为服务是在price赋值完成后才启动的,这里拿到的price一定是有效状态 res.render('index', { price }) }); // 必须主动调用启动函数,整个流程才会跑起来 bootStrap()
额外注意点
- 你代码里的
const JSON = require("JSON")属于冗余依赖:Node.js运行环境自带全局JSON对象,不需要额外引入这个包,这行代码可以直接删除,避免不必要的包体积开销。 - 验证变量赋值状态时,一定要把
console.log放在await取数完成的代码块内部,放在外部的同步位置永远打印不到异步赋值后的最终值。 - 尽量不要用全局变量存储请求级的临时数据:Express服务是并行处理多用户请求的,全局变量的状态会被所有请求共享,很容易出现请求间数据覆盖、拿到脏值的问题。
内容的提问来源于stack exchange,提问作者Sara Simon
相关产品推荐
相关产品推荐

