You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套axios调用首次执行时EJS渲染变量未定义报错问题

问题原因
  • 核心问题是Promise嵌套时没有打通调用链:你在第一层axios的then回调里发起了第二层axios请求,但没有将这个内层Promise返回,外层的异步流程没有真正等待内层请求执行完成。首次访问第三方接口时需要做DNS解析、TCP连接建立、SSL握手,整体请求耗时更长,时序问题直接暴露,render逻辑执行时h1等从接口取值的变量还没完成赋值,就会报undefined错误。
  • 第二次及之后访问时,Node.js、操作系统都已经缓存了接口的连接信息,请求耗时大幅缩短,回调执行顺序刚好和你预期一致,看起来运行正常,但这属于概率性正常,只要接口延迟出现波动问题就会复现。
  • 你在外层作用域声明共享let变量、靠异步回调修改变量值的写法本身存在并发风险:如果用户短时间内触发多个同路由请求,不同请求的变量值会互相覆盖,直接返回错误的页面数据。
  • 你当前的写法完全没有异常捕获逻辑,只要任意一个接口请求失败,服务会直接抛出未捕获的Promise错误,甚至导致进程退出。
修复方案

推荐用async/await写法重构,逻辑是线性的,完全避免嵌套Promise忘return导致的链断裂问题,也不需要在外层声明一堆待赋值的共享变量:

app.get('/private_:city/', async function(req, response) {
    try {
        const name = req.params.city;
        const nameCapitalised = name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
        
        // 等待第一个地理接口返回
        const geoRes = await axios.get(`http://api.openweathermap.org/geo/1.0/direct?q=${nameCapitalised}&limit=1&appid=${apiKeyOpenWeather}`);
        const { lat, lon } = geoRes.data[0];
        console.log('first', geoRes.data[0]);

        // 等待第二个天气接口返回
        const weatherRes = await axios.get(`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&exclude=daily,minutely&units=metric&lang=it&appid=${apiKeyOpenWeather}`);
        console.log('second');
        const currentHourData = weatherRes.data.hourly[0];
        
        // 直接在当前作用域处理要渲染的变量,不需要共享外层变量
        const h1 = new Date(currentHourData.dt * 1000).getHours();
        const w1 = currentHourData.weather[0].description;
        const t1 = currentHourData.temp;
        const uv1 = currentHourData.uvi;
        const rp1 = Math.trunc(currentHourData.pop * 100);
        const ws1 = currentHourData.wind_speed;

        console.log('third');
        // 所有变量准备完成再渲染
        response.render('private.ejs', {
            title: nameCapitalised,
            t1,
            h1,
            w1,
            uv1,
            rp1,
            ws1,
        });
    } catch (err) {
        // 加错误处理,接口失败时返回错误提示,避免服务崩溃
        console.error('接口请求失败', err);
        response.status(500).send('获取天气数据失败,请稍后重试');
    }
});

如果坚持用原来的then链式写法,必须保证每一层异步Promise都被return,把整条链打通,同样不要用外层共享变量:

app.get('/private_:city/', function(req, response) {
    const name = req.params.city;
    const nameCapitalised = name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();

    axios.get(`http://api.openweathermap.org/geo/1.0/direct?q=${nameCapitalised}&limit=1&appid=${apiKeyOpenWeather}`)
        .then(geoRes => {
            console.log('first', geoRes.data[0]);
            const { lat, lon } = geoRes.data[0];
            // 必须return内层的Promise,外层链才会等待它执行完成
            return axios.get(`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&exclude=daily,minutely&units=metric&lang=it&appid=${apiKeyOpenWeather}`);
        })
        .then(weatherRes => {
            console.log('second');
            const currentHourData = weatherRes.data.hourly[0];
            // 把处理好的渲染数据return给下一个then
            return {
                h1: new Date(currentHourData.dt * 1000).getHours(),
                w1: currentHourData.weather[0].description,
                t1: currentHourData.temp,
                uv1: currentHourData.uvi,
                rp1: Math.trunc(currentHourData.pop * 100),
                ws1: currentHourData.wind_speed
            };
        })
        .then(renderData => {
            console.log('third');
            response.render('private.ejs', {
                title: nameCapitalised,
                ...renderData
            });
        })
        .catch(err => {
            console.error('接口请求失败', err);
            response.status(500).send('获取天气数据失败,请稍后重试');
        });
});

注意:不要用外层作用域的共享变量在异步回调里传值,Node.js服务端的路由回调作用域会被并发请求复用,这种写法必然会出现请求间数据串扰的问题。

内容的提问来源于stack exchange,提问作者gab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 09:36:37