【问题标题】:Why doesn't 'await' wait for axios request completes?为什么“等待”不等待 axios 请求完成?
【发布时间】:2020-06-08 12:41:40
【问题描述】:

我尝试使用 axios 读取 HTTP 响应,并使用 stream-json 以流模式解析 JSON,这样我就可以按需完全适应我的数据库。它运行良好,但如果我尝试关闭数据库连接,一切都会崩溃,因为连接将很快关闭。 问题是:await 不会等待 extract_coins 函数完成(即使它正在返回承诺)并在最终范围内关闭数据库连接。

const main = async() => {
   const dbcfg = config.get('db.coins');
   const db = await coins_db_configure(dbcfg);
   try {
      console.log('Connected to the database and created coins table!');

      await extract_coins('some_url_which_gives_correct_json', coins_emitter);
   }
   catch(e){
      console.error(e);
   }
   finally {
      await db.close();
   }
   };

main();

extract_coins:

module.exports = async function extract_coins(url, emitter){
    return await axios({
        method: 'get',
        url: url,
        responseType: 'stream'
    }).then((res) => {
        const pipeline = chain([
            res.data,
            parser(),
            pick({filter: 'data'}),
            streamArray()
        ]);
        pipeline.on('data', data => {
            emitter.emit('coin_extracted', data.value);
        });
        pipeline.on('end', () => console.log("All the coins were successfully passed!"));
    });
};

【问题讨论】:

  • 您没有在提取函数中返回任何内容。
  • 您的管道发生了异步事情,这不是基于承诺的,因此不会等待。
  • @Phix 我返回 Promise 并希望 await 了解“结束” - 事件应该实现承诺
  • @trincot,就像你说的,我删除了我所有的 cmets。

标签: javascript json asynchronous async-await axios


【解决方案1】:

由于与异步pipeline 相关的代码没有被承诺,您目前无法返回一个承诺,该承诺会在获得“结束”事件时解决。

您的await 确实等待承诺解决,但您的then 回调返回undefined,因此在那一刻解决了承诺,早于end事件正在广播。

所以改变这个:

then((res) => {
    const pipeline = chain([
        res.data,
        parser(),
        pick({filter: 'data'}),
        streamArray()
    ]);
    pipeline.on('data', data => {
        emitter.emit('coin_extracted', data.value);
    });
    pipeline.on('end', () => console.log("All the coins were successfully passed!"));
});

到这里:

then((res) => new Promise((resolve) => {
    const pipeline = chain([
        res.data,
        parser(),
        pick({filter: 'data'}),
        streamArray()
    ]);
    pipeline.on('data', data => {
        emitter.emit('coin_extracted', data.value);
    });
    pipeline.on('end', () => {
        console.log("All the coins were successfully passed!");
        resolve();
    });
}));

【讨论】:

  • 我已经尝试了几个承诺和你的,但现在我遇到了另一个错误:连接已终止,extract_coins 的执行速度非常慢。为什么它在不关闭数据库连接的情况下运行得如此快速和良好?
  • 如果您删除try/catch,那么您会在哪个语句中得到错误?哪个 console.log 需要很长时间才能出现?你确定你只给main打过一次电话吗?
  • 但是,请理解这不是进行渐进式修复调试会话的地方。我想我回答了你的问题“为什么'await'不等待axios请求完成?”我愿意提供帮助,所以只需彻底调试以提供准确的信息。可能您需要就新问题提出新问题...
  • 你是对的,但 db.close() 和 extract_coins() 都不会导致连接终止错误,当我想 promisify 函数并在回调中解决它时会发生这种情况。
【解决方案2】:

实际上,您使用 ES6+ 方式但不是以通用方式,这会导致此问题,但肯定的是,将 awaitthen 一起使用是错误的,您应该像这样编写它们以下代码:

const main = async () => {
   try {
     const dbcfg = config.get('db.coins');
     const db = await coins_db_configure(dbcfg);
     console.log('Connected to the database and created coins table!');
     await extract_coins('some_url_which_gives_correct_json', coins_emitter);

   } catch (e) {
      console.error(e);

   } finally {
      await db.close();

   }
};

main();

如你所见,我把所有代码都放在了try 块中,因为使用async/await 意味着我们假装写了一个sync 代码,但实际上它是async,所以我们应该将所有代码,尤其是异步行放在try 块内。将它们放入 try 块后,只是因为 () 后面的 async 标志 JavaScript 解释器等待每一行完成,如果每一行有错误,下一行将不会运行并且解释器落入 catch 块, finally 像现在一样在所有情况下运行。

好的,现在回到主要问题,extract_coins 函数:

export default (async function extract_coins(url, emitter) {
  try {
    const res = await axios({
      method: 'get',
      url: url,
      responseType: 'stream'
    });
    const pipeline = chain([
      res.data,
      parser(),
      pick({filter: 'data'}),
      streamArray()
    ]);
    await pipeline.on('data', data => {
      emitter.emit('coin_extracted', data.value);
    });
    await pipeline.on('end', () => console.log("All the coins were successfully passed!"));
    // HERE: return what you want

  } catch (e) {
    throw e;

  }

});

这是问题的原因,您应该使用新的 EcmaScript 方法传递 extract_coins 函数的承诺,而不是使用回调函数。

如果我想代替你,我会写extract_coins,如下所示:

const extract_coins = async (url, emitter) => {
  try {
    const res = await axios({
      method: 'get',
      url: url,
      responseType: 'stream'
    });
    const pipeline = chain([
      res.data,
      parser(),
      pick({filter: 'data'}),
      streamArray()
    ]);
    await pipeline.on('data', data => {
      emitter.emit('coin_extracted', data.value);
    });
    await pipeline.on('end', () => console.log("All the coins were successfully passed!"));
    // HERE: return what you want

  } catch (e) {
    throw e;

  }

};

export default extract_coins;

【讨论】:

  • 一个关于如何在 try 范围内定义 db 的小问题,同时您尝试在 finally 范围内使用它。我不认为这是正确的,因为config.get() 函数可以抛出异常,最后作用域将无法正常工作,因为db 变量不存在。
猜你喜欢
  • 1970-01-01
  • 2019-12-18
  • 1970-01-01
  • 2011-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-19
  • 2022-10-01
相关资源
最近更新 更多