admin管理员组

文章数量:1356426

I am working in a project that uses Node.js for a Haraka (an smtp server) plugin.

This is Node.JS and I have a little problem whith callbacks. I haven't been able to convert this particular code to use a callback.

So, this is the code that I have:

exports.hook_data = function (next, connection) {
    connection.transaction.add_body_filter('', function (content_type, encoding, body_buffer) {
        var header = connection.transaction.header.get("header");
        if (header == null || header == undefined || header == '') return body_buffer;

        var url = '=' + header ;
        var request = require('request');
        request.get({ uri: url },
          function (err, resp, body) {
              var resultFromServer = JSON.parse(body);
              return ChangeBuffer(content_type, encoding, body_buffer, resultFromServer);
          }
        );
    });
    return next();
}

This code does not work because It doesn't wait the callback of the Request to continue. I need to finish the request before next();

And these are the requirements:

  1. At the end of exports.hook_data is mandatory to return next(). But it only have to return it after the request.
  2. I need to return a Buffer in add_body_filter but I need to create the buffer with information getted from a server.
  3. To make the Get Request I need to use a parameter (header) that I only have inside add_body_filter.

So the issue is that I can not make the request before add_body_filter and put the result inside a callback because the parameter that I need to make the request are only inside add_body_filter.

Any advice please?

I am working in a project that uses Node.js for a Haraka (an smtp server) plugin.

This is Node.JS and I have a little problem whith callbacks. I haven't been able to convert this particular code to use a callback.

So, this is the code that I have:

exports.hook_data = function (next, connection) {
    connection.transaction.add_body_filter('', function (content_type, encoding, body_buffer) {
        var header = connection.transaction.header.get("header");
        if (header == null || header == undefined || header == '') return body_buffer;

        var url = 'https://server./api?header=' + header ;
        var request = require('request');
        request.get({ uri: url },
          function (err, resp, body) {
              var resultFromServer = JSON.parse(body);
              return ChangeBuffer(content_type, encoding, body_buffer, resultFromServer);
          }
        );
    });
    return next();
}

This code does not work because It doesn't wait the callback of the Request to continue. I need to finish the request before next();

And these are the requirements:

  1. At the end of exports.hook_data is mandatory to return next(). But it only have to return it after the request.
  2. I need to return a Buffer in add_body_filter but I need to create the buffer with information getted from a server.
  3. To make the Get Request I need to use a parameter (header) that I only have inside add_body_filter.

So the issue is that I can not make the request before add_body_filter and put the result inside a callback because the parameter that I need to make the request are only inside add_body_filter.

Any advice please?

Share Improve this question asked Dec 15, 2015 at 20:16 Ricardo Polo JaramilloRicardo Polo Jaramillo 12.3k13 gold badges62 silver badges85 bronze badges 13
  • 1 You can't use an async function in Javascript, but demand that it behave synchronously. That is simply NOT possible. If you have an async operation, then you MUST code to use it asynchronously. That means returning results in a promise or via a callback, NOT via the return value from the function because the function will return BEFORE the async operation is even plete. You would probably benefit from reading this How to return response from asynchronous operation. – jfriend00 Commented Dec 15, 2015 at 22:30
  • Can you pass a callback to ChangeBuffer? – Alexander Elgin Commented Dec 18, 2015 at 7:00
  • And where do you use body_buffer (see the line #4) and the result of ChangeBuffer(content_type, encoding, body_buffer, resultFromServer) (see the line #11) which you return? I guess nowhere, right? – Alexander Elgin Commented Dec 18, 2015 at 7:22
  • @AlexanderElgin the return of add_body_filter is a buffer, this buffer contains the body of an email. I dont use it directly, the server uses it when processing an email. I write ChangeBuffer so I can pass a callback If needed. – Ricardo Polo Jaramillo Commented Dec 18, 2015 at 12:51
  • Can you use Async.js or Promises to control the flow? Otherwise, you'll just end up with a really hacky solution to be honest. – Quy Commented Dec 20, 2015 at 3:23
 |  Show 8 more ments

5 Answers 5

Reset to default 4

Unless you are willing to use synchronous, blocking functions, it is impossible to satisfy the requirements you have numbered.

I would examine reasons underlying each requirement and see if you acplish your goals in a different way.

On face value, looking at just the code you have there, I would look for a way to alter the contract of exports.hook_data so that you're able to call next() from inside the request.get callback.

Use Async.js or Promises.

Async implementation:

exports.hook_data = function (next, connection) {
  async.waterfall([
    function( done ) {
      connection.transaction.add_body_filter('', function( content_type, encoding, body_buffer ) {
        var header = connection.transaction.header.get("header");
        if ( header == null || header == undefined || header == '' ) {
          done(null, body_buffer);
        }
        done(null, header);
      });
    },
    function( header, done ) {
      var url = 'https://server./api?header=' + header;
      var request = require('request');
      request.get({ uri: url },
        function( err, resp, body ) {
          // do something if there's an error
          var resultFromServer = JSON.parse(body);
          done(null, ChangeBuffer(content_type, encoding, body_buffer, resultFromServer));
        }
      );
    }
  ], function( error, buffer ) {
    // do something if there's error
    next(buffer);
  });
}

There's a lot here and I remend you reading the docs on async.js#waterfall. Essentially it breaks up each async call into it's on function block and waits for it to return before it continues onto the next block.

Again, since these are all async, Node submits these tasks to the IO event loop and it's taken care of there (non-blocking). When this thread is waiting, it'll probably move on to the next request while this request is waiting.

Looking at the Haraka manual for Transaction object & Header object I don't see there is any dependency of Header on add_body_filter. Also your code doesn't show any dependency on add_body_filter. So your 3rd requirement looks invalid.

Considering that, I think following pseudocode(as I can't test it) should work for you.

exports.hook_data = function (next, connection) {
    var header = connection.transaction.header.get("header");
    if (header == null || header == undefined || header == '') {
        return next();

    var url = 'https://server./api?header=' + header ;
    var request = require('request');
    request.get({ uri: url },
        function (err, resp, body) {
            var resultFromServer = JSON.parse(body);
            connection.transaction.add_body_filter('', function (content_type, encoding, body_buffer) {
                return ChangeBuffer(content_type, encoding, body_buffer, resultFromServer);
            });
            next();
        }
    );
}

If Haraka manual has failed to highlight the dependency of Header on add_body_filter and / or you have discovered it based on your practical experience, then Quy's approach seems to be the way to go.

If you are wondering when to use next() v/s return next()

For handling priority of each part of your code I use Q, You can use it in few steps:

  1. npm install q.

    var request = require('request');
    var Q = require('q');
    
    exports.hook_data = function () {
       var url, header;
       Q()
       .then(function(){
         connection.transaction.add_body_filter('', function(content_type, encoding, body_buffer) {
    
          header = connection.transaction.header.get("header");
    
          if (header == null || header == undefined || header == ''){return body_buffer;}
          url = 'https://server./api?header=' + header ;
    
       })
       .then(function(){
    
           request.get({ uri: url }, function (err, resp, body) {
    
               var resultFromServer = JSON.parse(body);
    
               return ChangeBuffer(content_type,encoding,body_buffer,resultFromServer);
           });
    
       })
    
    }
    

In async scenarios, using q for callbacks is more better than other ways I think.

JavaScript is asynchronous in nature. Asynchronous is a programming pattern which provides the feature of non-blocking code i.e do not stop or do not depend on another function / process to execute a particular line of code. Ref: Codementor Article

From Wikipedia

Node.js provides an event-driven architecture and a non-blocking I/O API designed to optimize an application's throughput and scalability for real-time web applications

What can you do?

  1. Use a sleep/wait ie setTimeout(Not remended)
  2. Use some async lib like https://github./caolan/async (Remended)
  3. Use some promise lib like Q

本文标签: javascriptHow can I make a callback that requires info of its child functionStack Overflow