1

I want to promisify an ODM/ORM. How would you implement promise interface while having other methods such as find(), insert(), update() etc. So you could do

var Users = Collection('users')
Users.find({name: 'joe'})
  .then(users => users.update({name: 'jim'))
  .then(console.log)

I'm thinking of inheriting from Promise but how do you customize the then() to return your ORM instance to ensure we're working with the instance with all the queries made in sequence. Right now I'm using composition but then I have to proxy every method call which is getting ugly.

Eg:

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
}

What I'm trying to do is:

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)

I can't seem to get Promise to be initialized. Or should I be doing this a different way?

  • `I want to promisify` - I take it bluebirds `promisify` methods aren't suitable? http://bluebirdjs.com/docs/api/promise.promisify.html or http://bluebirdjs.com/docs/api/promise.promisifyall.html – Jaromanda X Jan 18 '17 at 00:23
  • promisify and promisifyAll will make the functions return a Promise instance. I need a ODM instance that inherits the promise methods returned so I can chain more ODM methods to the Promise. eg: Users.insert(...).catch(...).delete(...).then(). With promisify, once you do a then() or catch() you can't call another ODM method. – Gabiriele Lalasava Jan 18 '17 at 08:49
  • I don't get why you would want `Collection` to inherit from `Promise`. When/for what would you use something `Users.then(??? => { … })`? `Collection` should be a normal class, and each of the `find`/`insert`/`update` methods should return a promise for *its respective result*. Not a new collection. – Bergi Jan 19 '17 at 14:48
  • To make the collection functional and retain state - immutable or otherwise. Then you could make it more FP like and control the flow of data. Eg. `Users.create().then(log).catch(retryAfterAWhile).insert()` instead of `Users.create().then(state => Users.insert())`. But you're right, I might be overthinking this. I basically am looking for something like highland.js but for an ODM and with a spec interface like Promise. Am I misunderstanding Promises? – Gabiriele Lalasava Jan 19 '17 at 14:57

1 Answers1

1

After a bit of research I find that inheriting a promise in an ORM Instance is not a good idea because of the following:

Promises resolve recursively, each returning a new promise resolved from the last.

eg:

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

promise1 will first resolve promise2 and use the resolved value as the resolution value of promise1. promise1 will then return a new promise (promise3) resolved to the value of promise1.

This is important because promise resolution states are immutable yet promises are chainable.

To efficiently return a new promise for each then() call in an ODM while retaining it's current state would require optimizations such as making the ORM state data immutable or referenced from a global store (registry) while keeping it's current query unique (the filters, sorts, loaded relationships) etc. It's simpler to just compose the promise interface like then(), catch() onto the ORM as the Promise specification is quite forgiving.

Extending bluebird by inheritence is prevented since there is a check to only allow instance creation by an Object of type Promise. https://github.com/petkaantonov/bluebird/issues/325

Bluebird as well as the built in Promise in FF, Chrome and Node.js can't be extended via ES5 inheritence. They all require and instance of Promise to instantiate.

ECMA262 defines the Promise object inheritable. http://www.ecma-international.org/ecma-262/6.0/#sec-promise-constructor

Using ES6 class syntax you can extend the built in browser promise.

class MyPromise extends Promise {} 

This may work on Node.js using Babel.

You can extend both bluebird and the built-in promise by setting the __proto__ property directly in 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

Though I wouldn't recommend it as __proto__ is not standard though supported widely.