Использование возвратов и обратных вызовов в одной и той же функции.
Звучит как сумасшедший разговор, я знаю, но выслушай меня, у меня есть веская причина Думаю.
Допустим, вы хотите создать несколько TCP- серверов в приложении node.js. Приходится слушать на нескольких портах или что угодно. Использование заводской функции — лучший способ избежать повторения кода, верно?
Вы получите что-то вроде этого:
var server = function (port, callback) { var server = net.createServer(function (connection) {}); server.on("connection", function (socket) { socket.on("data", // data stuff); socket.on("error", function () { console.log("error?", arguments); socket.destroy(); }); socket.on("close", // cleanup stuff); }); server.on("listening", callback); server.listen(port); };
Просто. Вызовите функцию, назначьте ей порт, получите уведомление, когда сервер будет готов к прослушиванию. Никогда не сможет снова прикоснуться к серверу.
Подожди, это не хорошо. Что делать, если вы хотите сослаться на сервер позже? Чтобы закрыть это, например.
Вот где использование return вместе с обратными вызовами приходит.
Просто добавление return server
внизу этой функции позволяет нам сделать что-то вроде этого:
var make_servers = function (callback) { this.servers = {}; async.each([5000, 5001, 5002], _.bind(function (port, callback) { this.servers[port] = server(port, callback); }, this), function (err) { callback(); }); return this; }
Я украдкой добавил библиотеки async и underscore, потому что они облегчают жизнь.
make_servers
будет генерировать три сервера прослушивает порты 5000
в 5002
. Библиотека async помогла нам гарантировать, что основной обратный вызов вызывается только после того, как все серверы готовы к прослушиванию, а использование _.bind
позволяет привязать генерацию серверов к текущей области.
Когда эта функция выполнена, она возвращает свою область видимости, которая теперь включает ссылки на все серверы, и сообщит вашему коду продолжать работу, когда все серверы будут готовы.
Вы бы использовали что-то вроде этого:
var stuff = make_servers(function () { // all servers listening }); // stuff.servers can access all servers
Если вы привязываете все обратные вызовы внутри server
к текущей области, вы даже можете отслеживать соединения. Вы бы в конечном итоге что-то вроде этого:
var server = function (port) { var server = net.createServer(function (connection) {}); server.on("connection", _.bind(function (socket) { socket.id = shortid.generate(); this.connections[port][socket.id] = socket; socket.on("data", // data stuff); socket.on("error", function () { console.log("error?", arguments); socket.destroy(); }); socket.on("close", _.bind(function () { delete this.connections[port][socket.id]; }, this)); }, this)); return function (callback) { server.on("listening", callback); server.listen(port); return server; }; };
Не так много изменилось. Было связано больше вещей, this
и возвращаемое значение стало функцией, потому что я чувствую, что частичное применение делает этот код чище.
Вы бы назвать server
завод , как это сейчас: this.servers[port] = server(port)(callback)
. Основным преимуществом этого подхода является то, что мы можем генерировать серверы в цикле и активировать их позднее.
Мы по существу отделили генерацию сервера и запуск сервера. Может пригодиться.
stuff
Объект от лица теперь будет иметь , stuff.connections
а также, какие ссылки в настоящее время все открытые подключения к каждому порту. Ухоженная!
Еще одна уловка, которую я использовал в этих примерах, — это мощный динамический обзор Javascript . Получение полуслучайных функций, запускаемых в одной и той же области, может действительно очистить ваш код.
За счет того, что иногда он слишком умен. Используйте на свой страх и риск.
Как вы думаете, есть ли более чистый способ реализовать что-то подобное?