Реализация (наследование) интерфейса 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__ не является стандартным, хотя поддерживается широко.

Другие вопросы по тегам