Node.js和Express:在HTTP请求响应之前等待异步操作完成

3

假设有一个定义为HTTP GET回调函数:

router.get('/latestpost', function(req, res, next) {

    var data = new FbData();
    get_latest_post (data);
    get_post_image (data);

    res.json(data);
};
get_ 函数都使用 fb 包 来生成 HTTP 请求,并在完成后执行回调。如何修改上述 GET 回调以等待 Facebook 的响应,然后才向客户端发送响应?
目前,我通过依次执行 get_ 函数并传递它们的 res(响应)参数来解决了这个问题,最后一个函数发送响应。
router.get('/latestpost', function(req, res, next) {
    var data = new FbData();
    get_latest_post (res, data);
};


function get_latest_post (res, data) {

    FB.api(_url, function (res_fb) {

        if(!res_fb || res_fb.error) {
            console.log(!res_fb ? 'error occurred' : res_fb.error);
            return;
        }

        // Do stuff with data

        get_post_image (res, data);
    });
}


function get_post_image (res, data) {

    FB.api(_url, function (res_fb) {

        if(!res_fb || res_fb.error) {
            console.log(!res_fb ? 'error occurred' : res_fb.error);
            return;
        }

        // Do stuff with data

        /* At the end send the post data to the client */
        res.json(data);
    });
}

我找到了一个类似的问题(链接),但我仍然很困惑,因为我找不到一个适当的方法来将解决方案应用到我的问题上。我尝试使用这个手册中描述的模式,但我无法使用 promises 或 async/await 执行它。请问有人可以指点我正确的方向吗?
1个回答

6

您的API可以轻松地修改为返回一个Promise:

 function get_post_image (res, data) {
   return new Promise((resolve, reject) => {
    FB.api(_url, function (res_fb) {
      if(!res_fb || res_fb.error) {
        reject(res_fb && res_fb.error);
      } else resolve(res_fb/*?*/);
   });
 }

现在您已经有了一个Promise,您可以等待它:

 router.get('/latestpost', async function(req, res, next) {
   const data = new FbData();
   const image = await get_post_image (data);

   res.json(data);
});

2
最好将await包装在try中,并将捕获的错误传递给next(),以避免未捕获的拒绝承诺。但是,您不能使用util.callbackify(),因为它会在成功时使用两个参数调用next(),这是不希望发生的。 - Patrick Roberts
1
你可以定义 const callbackify = fn => Object.defineProperty( (...args) => fn(...args.slice(0, -1)).catch(args.pop()), 'length', { value: fn.length + 1, configurable: true } ); 来处理 Express 根据函数的参数数量推断中间件类型的方式,并避免在每个 async 中间件中显式地使用 try...catch - Patrick Roberts
非常正确。我可能会考虑在我的下一个项目中使用它。 - Patrick Roberts
@patrick 我承认我也还没有多少使用它的经验...我大多数时候都是使用Express,并在其周围添加了一些自制的包装器... - Jonas Wilms

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接