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

Fastify请求中异步函数未及时返回数据的解决方法

Fastify /pricing接口二次请求返回空数组的异步问题排查与解决

问题概述

在Fastify的/pricing接口中,首次请求能正常渲染数据,但二次请求仅返回空数组。尝试使用async/await但未生效,核心代码如下:

const db = require("playerio-node");

const index = async(fastify, options, done) => {

    fastify.get("/pricing", async(req, res) => {
        let items = [];
        db.PlayerIO.authenticate(
            "HIDDEN",
            "HIDDEN",
            {
                userId: "Stock",
                auth: require("../../../../../index").hashGenerator("Stock")
            },
            {},
             (client) => {
                client.bigDB.loadOrCreate("StockItems", "Stock", function (obj) {
                    req.config.stockItems.map(item => {
                        items.push({
                            name: item.name,
                            img: item.img,
                            price: item.price,
                            stock: obj[item.img] ?? 5
                        })
                    })
                    req.render("/dynamic/pricing/index.liquid", { stockItems: items, Chests: req.config.chests});
                });
            },
            (error) => {
                console.log(error)
            }
        );

        return req
    });

    done()
};

module.exports = index;

问题原因

  1. 异步流程未控制:db.PlayerIO.authenticate和client.bigDB.loadOrCreate都是回调风格的异步API,当前代码没等这些异步操作完成就直接return req,导致Fastify提前结束响应。首次请求可能因巧合(异步回调刚好在响应结束前执行)渲染了数据,但二次请求时异步回调执行滞后,无法正确写入响应。
  2. 响应返回方式错误:路由函数返回req不符合Fastify的响应规范,应该用reply对象处理响应。同时req.render在异步回调内调用,无法保证响应被正确处理。
  3. 错误处理缺失:原代码仅在错误回调中打印日志,未将错误抛出给Fastify处理,问题发生时无法及时定位。

解决方案

把回调风格的PlayerIO API包装成Promise,用async/await严格控制异步流程,同时规范Fastify的响应处理:

const db = require("playerio-node");
const hashGenerator = require("../../../../../index").hashGenerator;

const index = async(fastify, options, done) => {
    // 把PlayerIO authenticate包装成Promise
    const authenticate = () => {
        return new Promise((resolve, reject) => {
            db.PlayerIO.authenticate(
                "HIDDEN",
                "HIDDEN",
                { userId: "Stock", auth: hashGenerator("Stock") },
                {},
                resolve, // 成功时传递client
                reject   // 失败时传递error
            );
        });
    };

    // 把bigDB loadOrCreate包装成Promise
    const loadOrCreateStock = (client) => {
        return new Promise((resolve, reject) => {
            client.bigDB.loadOrCreate("StockItems", "Stock", resolve, reject);
        });
    };

    fastify.get("/pricing", async(req, reply) => {
        try {
            // 等待认证完成
            const client = await authenticate();
            // 等待数据加载完成
            const obj = await loadOrCreateStock(client);
            
            // 处理数据
            const items = req.config.stockItems.map(item => ({
                name: item.name,
                img: item.img,
                price: item.price,
                stock: obj[item.img] ?? 5
            }));

            // 用reply对象渲染并发送响应(根据你的render实现调整)
            const renderedHtml = await req.render("/dynamic/pricing/index.liquid", { 
                stockItems: items, 
                Chests: req.config.chests 
            });
            reply.send(renderedHtml);
        } catch (error) {
            // 抛出错误让Fastify处理
            reply.code(500).send({ error: error.message });
            console.error("Pricing接口错误:", error);
        }
    });

    done();
};

module.exports = index;

关键优化点

  • Promise包装:把所有回调式API转为Promise,确保async/await能正确等待异步操作完成,避免回调滞后导致的响应异常。
  • 响应规范:使用Fastify的reply对象处理响应,确保只有在数据完全处理完成后才发送响应,避免提前结束请求。
  • 错误处理:添加try/catch捕获所有异步错误,返回500状态码和错误信息,便于问题排查。
  • 代码可读性:提前解构引入hashGenerator,简化代码结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:13:33