如何在事件处理程序中进行异步调用 [英] How do I make asynchronous calls in an event handler

查看:72
本文介绍了如何在事件处理程序中进行异步调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个与具有HTTP API的第三方集成的自定义摩卡报告程序。



我举了一个自定义报告程序的基本示例( https://github.com/mochajs/mocha/wiki/Third-party-报告程序),并尝试在启动事件处理程序中添加对API的调用。但是,向第三方发送我的http请求的回调似乎从未触发。下面显示了示例代码。



我已经测试了请求代码在其他情况下是否可以正常工作,似乎只是在mocha报告程序的事件处理程序中,什么也没有



注意-请原谅当前的骇客等待回调的结果,我只想确保在回调有机会之前我没有退出开枪,我会在工作时整理一下!

  var request = require('request'); 

module.exports =函数(运行程序){
var pass = 0;
var failures = 0;

Runner.on('start',function(){
var callbackFired = false;
request('http://www.testapi.com',函数(错误,响应,正文){
callbackFired = true;
});
while(!callbackFired){
console.log('waiting ...');
}
});

Runner.on('pass',function(test){
pass ++;
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',通过,通过+失败);
进程.exit(failures);
});
};


解决方案

您的回调永远不会执行,因为您的代码确实您可能不了解JavaScript或暂时忘记了一个基本原则: JavaScript代码不会被抢占。保证该代码是一个无限循环:

  while(!callbackFired){
console.log('waiting ...');
}

为什么?首先,因为 request 必须在 callbackFired 变为真实之前实际完成HTTP请求。当您调用 request 时,它会启动HTTP请求并记录该请求完成后应调用该回调。第二,还记得我上面没有说过抢占吗? JavaScript VM在 callbackFired 变为true之前进入循环,并开始执行循环。循环执行过程中HTTP请求可能会完成,但回调 still 不会执行。为什么?因为只要循环正在执行,JavaScript VM便无法控制回调(无抢占!)。为了使VM能够控制回调,必须返回代码,以便最终由VM启动的所有功能都返回,然后VM有机会处理HTTP完成。



更正式的术语(请参见

这样说,您的代码不会给VM的事件循环提供机会来处理HTTP完成事件并为您的回调提供控制。 ,还有其他一些注意事项可让您的报告程序异步工作:


  1. 不要调用 process.exit ,如果您需要在进程退出时进行操作,则可以使用 process.on('exit',...


  2. 事件处理程序是同步运行的,因此您不能拥有 runner.on('start',...)等待异步操作的结果。



    Wh您可以做的是让 runner.on('start',...)启动一个异步操作,该操作返回一个Promise,然后在 runner中。 on('pass',...)(和'fail'),您将使用此承诺执行更多异步操作。例如,

      module.exports =函数(运行程序){
    var pass = 0;
    var failures = 0;
    var init;

    Runner.on('start',function(){
    init = async_op_returning_promise(...);
    });

    Runner.on('pass',function(test){
    pass ++;
    init.then(function(results){
    // //做更多的事情)。 ..
    });
    });



I'm trying to write a custom mocha reporter that integrates with a 3rd party that has a HTTP API.

I took the basic example given of a custom reporter (https://github.com/mochajs/mocha/wiki/Third-party-reporters) and attempted to add a call to the API in the start event handler. However, the callback for my http request to the third party never seems to fire. Example code shown below.

I've tested that the request code works as expected in other contexts, it just seems to be within the event handler in the mocha reporter that nothing happens.

Note - please forgive the current hack to wait on the result of the callback, I just want to make sure I wasn't exiting before the callback had a chance to fire, I'll tidy this up when I get it working!

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);
    });
};

解决方案

Your callback never executes because your code does not give it a chance to execute. There's a basic principle you either do not know about JavaScript or that you have momentarily forgotten: JavaScript code is not preempted. This code is guaranteed to be an infinite loop:

while(!callbackFired){
    console.log('waiting...');
}

Why? First, because request has to actually complete the HTTP request before callbackFired can become true. When you call request, what it does is initiate the HTTP request and record that when the request is completed, the callback should be called. Second, remember I said no preemption above? The JavaScript VM gets to the loop before callbackFired becomes true, and it starts executing the loop. It is possible that the HTTP request will complete while the loop is executing but the callback still won't execute. Why? Because the JavaScript VM cannot give control to the callback as long as the loop is executing (no preemption!). For the VM to be able to give control to the callback, your code has to return, so that eventually all functions that were started by the VM return, and the VM then gets the chance to handle the HTTP completion.

In more formal terms (see the documentation here, your code does not give the opportunity to the VM's event loop to process the HTTP completion event and give control to your callback.

This being said, there are other considerations to get your reporter working asynchronously:

  1. Don't call process.exit from your reporter. If you need things to happen when the process exits, you can use process.on('exit', ....

  2. The event handlers operate synchronously so you cannot have runner.on('start', ...) wait for the result of an asynchronous operation.

    What you could do is have runner.on('start', ...) initiate an asynchronous operation that returns a promise and then in runner.on('pass', ...) (and 'fail') you'd use this promise to perform more asynchronous operations. For instance,

    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...
        });
    });
    

这篇关于如何在事件处理程序中进行异步调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆