Как я могу сделать обратный вызов, который требует информации о его дочерней функции

Я работаю над проектом, который использует Node.js для плагина Haraka (smtp server).

Это Node.JS, и у меня есть небольшая проблема с обратными вызовами. Я не смог преобразовать этот код для использования обратного вызова.

Итак, это код, который у меня есть:

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

Этот код не работает, потому что он не ждет ответа на запрос запроса. Мне нужно завершить запрос до next();

И вот эти требования:

  • В конце exports.hook_data обязателен возврат next(). Но его нужно вернуть только после запроса.
  • Мне нужно вернуть Buffer в add_body_filter, но мне нужно создать буфер с информацией, полученной с сервера.
  • Чтобы получить запрос на получение, мне нужно использовать параметр (header), который у меня есть только внутри add_body_filter.

Итак, проблема в том, что я не могу выполнить запрос до add_body_filter и поместить результат в обратный вызов, потому что параметр, который мне нужен для запроса, находится только внутри add_body_filter.

Любые советы, пожалуйста?

Ответы

Ответ 1

Если вы не хотите использовать синхронные блокирующие функции, невозможно удовлетворить требования, которые вы пронумеровали.

Я бы рассмотрел причины, лежащие в основе каждого требования, и посмотрим, выполняете ли вы свои цели по-другому.

На лицевой стороне, глядя только на код, который у вас есть, я хотел бы найти способ изменить контракт exports.hook_data, чтобы вы могли вызвать next() из обратного вызова request.get.

Ответ 2

Используйте Async.js или Promises.

Асинхронная реализация:

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.com/api?header=' + header;
      var request = require('request');
      request.get({ uri: url },
        function( err, resp, body ) {
          // do something if there an error
          var resultFromServer = JSON.parse(body);
          done(null, ChangeBuffer(content_type, encoding, body_buffer, resultFromServer));
        }
      );
    }
  ], function( error, buffer ) {
    // do something if there error
    next(buffer);
  });
}

Здесь много, и я рекомендую вам читать документы на async.js # waterfall. По сути, он разбивает каждый асинхронный вызов на него на функциональном блоке и ждет, пока он вернется, прежде чем он перейдет к следующему блоку.

Опять же, поскольку все они являются асинхронными, Node отправляет эти задачи в цикл событий ввода-вывода, и он позаботился о нем (не блокирует). Когда этот поток ожидает, он, вероятно, перейдет к следующему запросу, пока этот запрос ожидает.

Ответ 3

Глядя на руководство Haraka для Объект транзакции и Объект заголовка Я не вижу зависимости от заголовка на add_body_filter. Также ваш код не показывает зависимости от add_body_filter. Поэтому ваше третье требование выглядит недействительным.

Учитывая это, я думаю, что следующий псевдокод (поскольку я не могу его протестировать) должен работать для вас.

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

Если руководство Haraka не смогло выделить зависимость заголовка на add_body_filter и/или вы обнаружили его на основе вашего практического опыта, то подход Quy, похоже, будет способом.

Если вам интересно когда использовать следующий() v/s return next()

Ответ 4

Для обработки приоритета каждой части кода я использую Q, вы можете использовать его в несколько шагов:

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

В сценариях async использование Q для обратных вызовов лучше, чем другие способы, о которых я думаю.

Ответ 5

JavaScript является асинхронным по своей природе. Асинхронный - это программирование шаблон, который обеспечивает функцию неблокирующего кода, а не остановить или не зависеть от другой функции/процесса, чтобы выполнить конкретную строку кода. Ссылка: Статья в кодексе

Из Википедии

Node.js предоставляет управляемую событиями архитектуру и неблокирующий API ввода-вывода, предназначенный для оптимизации пропускной способности и масштабируемости приложений для веб-приложений реального времени.

Что вы можете сделать?

  • Используйте sleep/wait ie setTimeout (не рекомендуется)
  • Используйте некоторые async lib, например https://github.com/caolan/async (рекомендуется)
  • Используйте некоторые обещания lib, например Q