Реализация (наследование) интерфейса Promise в ORM
Я хочу пообещать ODM/ORM. Как бы вы реализовали интерфейс обещаний, имея другие методы, такие как find()
, insert()
, update()
и т.д., так что вы могли бы сделать
var Users = Collection('users')
Users.find({name: 'joe'})
.then(users => users.update({name: 'jim'))
.then(console.log)
Я думаю о наследовании от Promise
но как вы настраиваете then() для возврата вашего экземпляра ORM, чтобы убедиться, что мы работаем с экземпляром со всеми запросами, выполненными в последовательности. Прямо сейчас я использую композицию, но затем я должен проксировать каждый вызов метода, который становится ужасным.
Например:
var Promise = require('bluebird')
var Collection = function(Storage, name) {
this.promise = new Promise(function(resolve, reject) {
self.resolve = resolve
self.reject = reject
})
}
Collection.prototype.find = function(query) {
// async query stuff here
Storage.doAsyncQueryStuff(function (err, results) {
err && this.reject(err)
this.resolve(results)
})
}
Collection.prototype.then = function(callback) {
var self = this
this.promise.then(function() {
callback && callback.apply(self, arguments)
})
return this
}
То, что я пытаюсь сделать, это:
var inherits = require('util').inherits
var Promise = require('bluebird')
var Collection = function(Storage, name) {
var self = this
// error here
Promise.call(
this,
function(resolve, reject) {
self.resolve = resolve
self.reject = reject
})
}
inherits(Collection, Promise)
Кажется, я не могу получить Promise для инициализации. Или я должен делать это по-другому?
1 ответ
После небольшого исследования я обнаружил, что наследование обещания в экземпляре ORM не очень хорошая идея из-за следующего:
Обещания решаются рекурсивно, каждый возвращает new promise
решено с последнего.
например:
var debug = console.log
var promise1 = new Promise((r, e) => {
r(1)
})
var promise2 = new Promise((r, e) => {
r(2)
})
var promise3 = promise1.then(data => promise2)
debug('promise1 === promise1', promise1 === promise1) // true
debug('promise3 === promise1', promise3 === promise1) // false
debug('promise3 === promise2', promise3 === promise2) // false
Обещание1 сначала разрешает обещание2 и использует разрешенное значение в качестве значения разрешения обещания1. Обещание1 вернет новое обещание (обещание3), разрешенное до значения обещания1.
Это важно, потому что состояния разрешения обещаний являются неизменными, но обещания являются цепными.
Чтобы эффективно возвращать новое обещание для каждого вызова then() в ODM при сохранении его текущего состояния, потребуются оптимизации, например, сделать данные состояния ORM неизменяемыми или ссылаться на них из глобального хранилища (реестра), сохраняя его текущим query
уникальные (фильтры, сортировки, загруженные отношения) и т. д. Проще просто создать интерфейс обещаний, например then(), catch(), в ORM, так как спецификация Promise довольно проста.
Расширение bluebird с помощью наследования запрещено, поскольку существует проверка, разрешающая создание экземпляров только с помощью объекта типа Promise. https://github.com/petkaantonov/bluebird/issues/325
Bluebird, а также встроенные Promise в FF, Chrome и Node.js не могут быть расширены с помощью наследования ES5. Все они требуют и экземпляр Promise для создания экземпляра.
ECMA262 определяет объект Promise наследуемый. http://www.ecma-international.org/ecma-262/6.0/
Используя синтаксис класса ES6, вы можете расширить встроенное обещание браузера.
class MyPromise extends Promise {}
Это может работать на Node.js с использованием Babel.
Вы можете расширить как Bluebird, так и встроенное обещание, установив __proto__
собственность прямо в ES5.
/**
* @class MyPromise
* @extends Promise
*/
var MyPromise = function () {
var resolve, reject
var promise = new Promise(function(_resolve, _reject) {
resolve = _resolve
reject = _reject
})
promise.__proto__ = this.__proto__
promise.resolve = resolve
promise.reject = reject
return promise
}
MyPromise.prototype = Object.create(Promise.prototype, { constructor: { value: MyPromise } })
MyPromise.prototype.call = function() {
this.resolve(new Date)
}
MyPromise.all = Promise.all
MyPromise.cast = Promise.cast
MyPromise.reject = Promise.reject
MyPromise.resolve = Promise.resolve
module.exports = MyPromise
Хотя я бы не рекомендовал это как __proto__
не является стандартным, хотя поддерживается широко.