当 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
块中的连接。
我创建了一个 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
块中的连接。