Имитация $.Deferred под node.js
Nov. 13th, 2014 10:01 pmПридумалась тут на днях офигенная концепция как на клиенте, в браузере, разрабатывать и отлаживать серверный js-код, работающий с БД. Из картиночки справа в общем всё понятно.
В нашем типичном стеке серверных технологий – node.js + express + cradle + CouchDB – достаточно заменить только cradle.js на PouchDB.
Обвес, позволяющий проделывать весь этот трюк в браузере, оказался на удивление несложным в целом. Я его как proof of concept написал за вечер, а на следующий вечер у меня уже заработал серверный код, полностью написанный в нашей самопальной IDE.
Как ни странно, всё взлетело сразу почти, без особого бубна с функционалом БД. Споткнулся я сначала на криптографии, но это мы проработали прошлой зимой, оставалось только чуток подкрутить. Гораздо более серьёзным оказался затык на библиотеке, обеспечивающий Promise-функционал нужного синтаксиса.
Промисы – модная и очень удобная штука, мы активно используем. Коллбэки – фу, промисы – кул. Беда в том, что синтаксис промисов иногда существенно отличается от библиотеки к библиотеке, там нет единого стандарта.
Исторически так сложилось, что на клиенте мы используем модель jQuery.Deferred – которая не очень то удачна, по-честному. Ну и оказалось что для node.js нет либы, которая повторяла бы синтаксис $.Deferred – чему я был очень удивлён.
В результате перебора bluebird.js, затем lie.js [ага, var Promise = require("lie")] и затем Q.js оказалось, что проще всего имитировать $.Deferred под нодом с помощью Q. Ну, как известно “то, что вы ищете, вы найдёте в самом последнем месте”, да.
Так вот, в самом простейшем варианте код выделки симулякра $.Deferred из Q.defer умещается в 20 строк:
var Q = require("q"); var Promise = function(){ var q = Q.defer(), qp = q.promise, pi = { then:function(a,b){return qp.then(a,b);}, fail:function(a,b){return qp.fail(a,b);}, done:function(a,b){return qp.done(a,b);}, progress:function(a,b){return qp.progress(a,b);}, promise: function(){return q.promise;}, resolve:function(a,b){q.resolve(a,b); return pi;}, reject:function(a,b){q.reject(a,b); return pi;}, notify:function(a,b){q.notify(a,b); return pi;}, isResolved:function(a,b){return q.isFulfilled(a,b);}, isRejected:function(a,b){return q.isRejected(a,b);}, state: function(){ var state = qp.inspect().state; return state==="fulfilled"?"resolved":state; } }; return pi; }
Если не заигрывать сильно с this, не засовывать синхронный код в промис и использовать только базовый синтаксис, этого достаточно. Вдруг кому пригодится.
Сегодня кста выяснилось, что node.js даже последних сборок использует уже не поддерживаемую Гуглом версию V8. А нод много где в продакшене используется о_О