Nested asynchronous objects are a problem with Promises/A+, since you can't nest promises.
Compare Data.Task:
function AsyncMap() {
this.data = {}
}
// :: String, a -> Task<b, Unit>
AsyncMap.prototype.set = function(key, value) {
return new Task(function(_, resolve) {
this.data[key] = value
resolve()
})
}
// :: String -> Task<Error, a>
AsyncMap.prototype.get = function(key, value) {
return new Task(function(reject, resolve) {
if (key in this.data) resolve(this.data[key])
else reject(new Error(key + ' not found'))
})
}
var x = new AsyncMap()
x.set('foo', Task.of(1))
x.get('foo').map(identity) // => Task(1)
With Promises/A+:
function AsyncMap() {
this.data = {}
}
// :: String, a -> Promise<b, Unit>
AsyncMap.prototype.set = function(key, value) {
return new Promise(function(_, resolve) {
this.data[key] = value
resolve()
})
}
// :: String -> Promise<Error, a>
AsyncMap.prototype.get = function(key, value) {
return new Promise(function(reject, resolve) {
if (key in this.data) resolve(this.data[key])
else reject(new Error(key + ' not found'))
})
}
var x = new AsyncMap()
x.set('foo', Promise.of(1))
x.get('foo').then(identity) // => 1
To get the promise-based version to work correctly you need to wrap all values:
function Wrap(v) {
this.value = v
}
function AsyncMap() {
this.data = {}
}
// :: String, a -> Promise<b, Unit>
AsyncMap.prototype.set = function(key, value) {
return new Promise(function(_, resolve) {
this.data[key] = value
resolve()
})
}
// :: String -> Promise<Error, Wrap<a>>
AsyncMap.prototype.get = function(key, value) {
return new Promise(function(reject, resolve) {
if (key in this.data) resolve(new Wrap(this.data[key]))
else reject(new Error(key + ' not found'))
})
}
var x = new AsyncMap()
x.set('foo', Promise.of(1))
x.get('foo').then(identity) // => Wrap(Promise(1))