当 Promise 拒绝时,Node js 停止长时间的处理任务

Node js stop long process task when Promise reject it

我创建了一个 promise 函数来处理一个长时间的查询任务。有时任务会阻塞数小时。我想设置一个超时来停止任务。下面是代码。

它可以return错误消息正确,但它仍然运行 connection.execute() 很长一段时间才停止。那么如何在return拒绝消息时立即停止它呢?

谢谢!

function executeQuery(connection, query) {
return new Promise((resolve, reject) => {
    "use strict";
    //long time query
    connection.execute(query, function (err, results) {
        if (err) reject('Error when fetch data');
        else resolve(results);
        clearTimeout(t);
    });

    let t = setTimeout(function () {
        reject('Time Out');
    }, 10);
})


(async () => {
"use strict";
oracle.outFormat = oracle.OBJECT;

try {
    let query = fs.readFileSync("query.sql").toString();
    let results = await executeQuery(connection, query);

    console.log(results.rows);
} catch (e) {
    console.log(`error:${e}`);

}

试试这个(使用 bluebird 承诺):

var execute = Promise.promisify(connection.execute);

function executeQuery(connection, query) {
   return execute.call(connection, query)
   .timeout(10000)
   .then(function (results) {
      // handle results here
   })
   .catch(Promise.TimeoutError, function (err) {
      // handle timeout error here
   });
   .catch(function (err) {
      // handle other errors here
   });
};

如果仍然阻塞,则有可能您使用的数据库驱动程序实际上是同步的而不是异步的。在那种情况下,该驱动程序将与节点事件循环不兼容,您可能需要查看另一个驱动程序。

正如 Bergi 提到的,您需要使用 connection.break 方法。

给出以下函数:

create or replace function wait_for_seconds(
  p_seconds in number
)
  return number
is
begin
  dbms_lock.sleep(p_seconds);

  return 1;
end;

这是一个使用示例:

const oracledb = require('oracledb');
const config = require('./dbConfig.js');
let conn;
let err;
let timeout;

oracledb.getConnection(config)
  .then((c) => {
    conn = c;

    timeout = setTimeout(() => {
      console.log('Timeout expired, invoking break');

      conn.break((err) => {
        console.log('Break finished', err);
      });
    }, 5000);

    return conn.execute(
     `select wait_for_seconds(10)
      from dual`,
      [],
      {
        outFormat: oracledb.OBJECT
      }
    );
  })
  .then(result => {
    console.log(result.rows);

    clearTimeout(timeout);
  })
  .catch(err => {
    console.log('Error in processing', err);

    if (/^Error: ORA-01013/.test(err)) {
      console.log('The error was related to the timeout');
    }
  })
  .then(() => {
    if (conn) { // conn assignment worked, need to close
      return conn.close();
    }
  })
  .catch(err => {
    console.log('Error during close', err)
  });

请记住,setTimeout 调用就在执行之前(因为 return 语句)。该超时将立即开始倒计时。但是,执行调用不能保证立即开始,因为它使用线程池中的一个线程,并且可能必须等到一个线程可用。只是要记住一些事情...

So how can stop it immediately when it return reject message?

根据文档,您可以使用 connection.break:

return new Promise((resolve, reject) => {
    connection.execute(query, (err, results) => {
        if (err) reject(err);
        else resolve(results);
        clearTimeout(t);
    });

    const t = setTimeout(() => {
        connection.break(reject); // is supposed to call the execute callback with an error
    }, 10);
})

确保release finally 块中的连接。