Как я могу сделать обратный вызов, который требует информации о его дочерней функции
Я работаю над проектом, который использует 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