使用外部时间限制在 Node/Express 中链接异步等待调用

Chaining async await calls in Node/Express with an external time limit

我正在构建一个调用 Express 应用程序的 Slackbot,然后需要 1) 从 Slack API 获取一些其他数据,以及 2) 将结果数据插入我的数据库。我想我最终使用 async await 的流程是正确的,但是操作超时,因为来自 Slackbot 的原始调用需要在我无法控制的某个固定时间内收到响应。就我的目的而言,立即通过响应 ping 机器人,然后异步执行其余逻辑就可以了。但我想知道设置它的最佳方法。

我的 Express 路线如下:

const express = require('express');
const router = express.Router();
const knex = require('../../db/knex.js');

const slack = require('../../services/slack_helpers');

// POST api/slack/foo
router.post('/foo', async (req, res) => {
    let [body, images] = await slack.grab_context(req);
    knex('texts')
        .insert({ body: body,
                  image_ids: images })
        .then(text => { res.send('worked!'); }) // This sends a response back to the original Slackbot call
        .catch(err => { res.send(err); })
});

module.exports = router;

然后 slack_helpers 模块看起来像:

const { WebClient } = require('@slack/web-api');
const Slack = new WebClient(process.env.SLACKBOT_TOKEN);

async function grab_context(req) {

    try {
        const context = await Slack.conversations.history({ // This is the part that takes too long
            channel: req.body.channel_id,
            latest: req.headers['X-Slack-Request-Timestamp'],
            inclusive: true,
            limit: 5
        });

    } catch (error) {
        return [error.toString(), 'error'];
    }
    return await parse_context(context);
};

function parse_context(context) {
    var body = [];

    context.messages.forEach(message => {
        body.push(message.text);
    });

    body = body.join(' \n');

    return [body, ''];
}

module.exports = {
    grab_context
};

我仍然在研究异步编程,所以我可能遗漏了一些明显的东西。我认为基本上 res.send 之类的东西可能需要在 grab_context 调用之前出现?但同样,不确定这里的最佳流量。

更新

我也在 API 路由中尝试过这种模式,但仍然超时:

slack.grab_context(req).then((body, images) => {
        knex ...
})

你的超时可能不是你想的那样。据我所知,它来自 grab_context。考虑以下 grab_context

的简化版本
async function grab_context_simple() {
  try {
    const context = { hello: 'world' }
  } catch (error) {
    return [error.toString(), 'error']
  }
  return context
}
grab_context_simple() /* => Promise {
  <rejected> ReferenceError: context is not defined
  ...
} */

您正在尝试 return context 在定义它的 try 块之外,因此 grab_context 将以 ReferenceError 拒绝。很可能这个错误此刻正在被吞噬,所以它看起来像是超时了。

修复方法是在 grab_context

中移动一行
async function grab_context(req) {

    try {
        const context = await Slack.conversations.history({
            channel: req.body.channel_id,
            latest: req.headers['X-Slack-Request-Timestamp'],
            inclusive: true,
            limit: 5
        });

        return await parse_context(context); // <- moved this
    } catch (error) {
        return [error.toString(), 'error'];
    }
};

I'm wondering the best way to set this up.

您可以添加更高级别的 try/catch 块来处理由 /foo 路由引起的错误。您还可以通过在 async/await 和承诺链之间保持一致来提高可读性。下面是如何将 async/await 与 knex 以及前面提到的 try/catch

一起使用
const express = require('express');
const router = express.Router();
const knex = require('../../db/knex.js');

const slack = require('../../services/slack_helpers');

const insertInto = table => payload => knex(table).insert(payload)

const onFooRequest = async (req, res) => {
  try {
    let [body, images] = await slack.grab_context(req);
    const text = await insertInto('texts')({
      body: body,
      image_ids: images,
    });
    res.send('worked!');
  } catch (err) {
    res.send(err);
  }
}

router.post('/foo', onFooRequest);

module.exports = router;