如何在事件处理程序中进行异步调用
How do I make asynchronous calls in an event handler
我正在尝试编写一个自定义 mocha 记者,它与具有 HTTP API 的第 3 方集成。
我采用了自定义报告程序 (https://github.com/mochajs/mocha/wiki/Third-party-reporters) 给出的基本示例,并尝试在启动事件处理程序中添加对 API 的调用。但是,我对第三方的 http 请求的回调似乎从未触发过。示例代码如下所示。
我已经测试过请求代码在其他上下文中按预期工作,它似乎只是在 mocha 报告器的事件处理程序中没有任何反应。
注意 - 请原谅当前的 hack 等待回调的结果,我只是想确保在回调有机会触发之前我没有退出,我会在收到时整理有效!
var request = require('request');
module.exports = function (runner) {
var passes = 0;
var failures = 0;
runner.on('start', function() {
var callbackFired = false;
request('http://www.testapi.com', function (error, response, body) {
callbackFired = true;
});
while(!callbackFired){
console.log('waiting...');
}
});
runner.on('pass', function(test){
passes++;
console.log('pass: %s', test.fullTitle());
});
runner.on('fail', function(test, err){
failures++;
console.log('fail: %s -- error: %s', test.fullTitle(), err.message);
});
runner.on('end', function(){
console.log('end: %d/%d', passes, passes + failures);
process.exit(failures);
});
};
你的回调永远不会执行,因为你的代码没有给它执行的机会。有一个基本原则你要么不知道 JavaScript,要么你知道暂时忘记:JavaScript 代码未被抢占。此代码保证是一个无限循环:
while(!callbackFired){
console.log('waiting...');
}
为什么?首先,因为 request
必须在 callbackFired
变为真之前实际完成 HTTP 请求。当你调用 request
时,它所做的是发起 HTTP 请求并记录当请求完成时应该调用回调。其次,还记得我在上面说过没有抢占吗? JavaScript VM 在 callbackFired
变为真之前进入循环,并开始执行循环。 HTTP 请求可能会在循环执行时完成,但回调 still 不会执行。为什么?因为只要循环正在执行,JavaScript VM 就无法控制回调(无抢占!)。为了让 VM 能够控制回调,您的代码必须 return,以便最终由 VM return 启动的所有函数,然后 VM 有机会处理HTTP 完成。
用更正式的术语来说(参见 documentation here,您的代码没有为 VM 的事件循环提供机会来处理 HTTP 完成事件并控制您的回调。
话虽这么说,但要让您的记者异步工作还有其他注意事项:
不要给你的记者打电话 process.exit
。如果您需要在进程退出时发生一些事情,您可以使用 process.on('exit', ...
.
事件处理程序同步运行,因此您不能runner.on('start', ...)
等待异步操作的结果。
你可以做的是让 runner.on('start', ...)
启动一个异步操作,return 是一个承诺,然后在 runner.on('pass', ...)
(和 'fail'
)中你将使用这个承诺执行更多的异步操作。例如,
module.exports = function (runner) {
var passes = 0;
var failures = 0;
var init;
runner.on('start', function() {
init = async_op_returning_promise(...);
});
runner.on('pass', function(test){
passes++;
init.then(function (results) {
// do something more...
});
});
我正在尝试编写一个自定义 mocha 记者,它与具有 HTTP API 的第 3 方集成。
我采用了自定义报告程序 (https://github.com/mochajs/mocha/wiki/Third-party-reporters) 给出的基本示例,并尝试在启动事件处理程序中添加对 API 的调用。但是,我对第三方的 http 请求的回调似乎从未触发过。示例代码如下所示。
我已经测试过请求代码在其他上下文中按预期工作,它似乎只是在 mocha 报告器的事件处理程序中没有任何反应。
注意 - 请原谅当前的 hack 等待回调的结果,我只是想确保在回调有机会触发之前我没有退出,我会在收到时整理有效!
var request = require('request');
module.exports = function (runner) {
var passes = 0;
var failures = 0;
runner.on('start', function() {
var callbackFired = false;
request('http://www.testapi.com', function (error, response, body) {
callbackFired = true;
});
while(!callbackFired){
console.log('waiting...');
}
});
runner.on('pass', function(test){
passes++;
console.log('pass: %s', test.fullTitle());
});
runner.on('fail', function(test, err){
failures++;
console.log('fail: %s -- error: %s', test.fullTitle(), err.message);
});
runner.on('end', function(){
console.log('end: %d/%d', passes, passes + failures);
process.exit(failures);
});
};
你的回调永远不会执行,因为你的代码没有给它执行的机会。有一个基本原则你要么不知道 JavaScript,要么你知道暂时忘记:JavaScript 代码未被抢占。此代码保证是一个无限循环:
while(!callbackFired){
console.log('waiting...');
}
为什么?首先,因为 request
必须在 callbackFired
变为真之前实际完成 HTTP 请求。当你调用 request
时,它所做的是发起 HTTP 请求并记录当请求完成时应该调用回调。其次,还记得我在上面说过没有抢占吗? JavaScript VM 在 callbackFired
变为真之前进入循环,并开始执行循环。 HTTP 请求可能会在循环执行时完成,但回调 still 不会执行。为什么?因为只要循环正在执行,JavaScript VM 就无法控制回调(无抢占!)。为了让 VM 能够控制回调,您的代码必须 return,以便最终由 VM return 启动的所有函数,然后 VM 有机会处理HTTP 完成。
用更正式的术语来说(参见 documentation here,您的代码没有为 VM 的事件循环提供机会来处理 HTTP 完成事件并控制您的回调。
话虽这么说,但要让您的记者异步工作还有其他注意事项:
不要给你的记者打电话
process.exit
。如果您需要在进程退出时发生一些事情,您可以使用process.on('exit', ...
.事件处理程序同步运行,因此您不能
runner.on('start', ...)
等待异步操作的结果。你可以做的是让
runner.on('start', ...)
启动一个异步操作,return 是一个承诺,然后在runner.on('pass', ...)
(和'fail'
)中你将使用这个承诺执行更多的异步操作。例如,module.exports = function (runner) { var passes = 0; var failures = 0; var init; runner.on('start', function() { init = async_op_returning_promise(...); }); runner.on('pass', function(test){ passes++; init.then(function (results) { // do something more... }); });