How do I make asynchronous calls in an event handler

冷暖自知 提交于 2020-01-15 04:59:07

问题


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

回答1:


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


来源:https://stackoverflow.com/questions/34653702/how-do-i-make-asynchronous-calls-in-an-event-handler

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!