Promise API

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
33 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

Herby Vojčík


[hidden email] wrote:

> On Tue, Jan 26, 2016 at 5:56 PM, Herby Vojčík <[hidden email]
> <mailto:[hidden email]>> wrote:
>
>
>
>     Dňa 26. januára 2016 16:54:51 CET používateľ "[hidden email]
>     <mailto:[hidden email]>" <[hidden email]
>     <mailto:[hidden email]>> napísal:
>      >I faced internal resistance at first when meeting promises but as
>     it is
>      >what makes the "modern" web what it (e.g. angular style coding), we
>
>     I don't know what you mean by angular style coding. I do not use
>     angular but use promises (and ES6 ones, not q, bluebird or any other
>     lib).
>
>
> JS with promises style.
>
>
>      >need to
>      >embrace that.
>      >
>      >Otherwise, it is nightmarish to use all of the APIs etc.
>      >
>      >Parens are no problem for me FWIW.
>
>     In JS, I find it ok. In Amber, parentheses would make me mad (hence,
>     chaining helper of this or that sort). But as I said, I find 8
>     members of [all:]then:[on:do:][catch:] family with array in then:
>     feasible (or 6 members of
>     [all:]then:[on:do:|catch:]).
>
>
> yes, this looks a nice avenue to go down.
>
>
>      >How would you see reject used?
>
>     I don't understand. You mean Promise.reject convenience API or
>     reject in executor?
>
>
> Promise.reject

I don't know if you use it often. It is a convenience method, it can be
always be done via executor, so presently I do not see the need to wrap
it, as I wrote. This should be enough:

   Promise asyncFromBlock: [ :model | model signal: 'foo' ]

But it's IMO.

>      >Phil

Herby

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

Herby Vojčík
In reply to this post by Herby Vojčík
Second version, after Philippe Back's suggestions:

Object >> asPromise
   "returns a Promise resolved to self"
   <return JS_Promise.resolve(self)>

JSObjectProxy >> asPromise
   <return JS_Promise.resolve(self["@jsObject"])>

Promise class >> forBlock: aBlock
   "Inspired by GNU Smalltalk API.
   Returns a Promise fulfilled or rejected by `aBlock value`.
   Only usable for synchronous operations in aBlock."
   ^ aBlock valueAsPromise

BlockClosure >> valueAsPromise
   <return JS_Promise.resolve().then(function () {return self._value()})>

Promise class >> asyncForBlock: aBlock
   "aBlock is called with PromiseModel object.
   `aPromiseModel value: x` resolves the promise.
   `aPromiseModel signal: y` rejects the promise.
   Returns a Promise that eventually
   succeeds or fails, asynchronously."
   ^ aBlock newPromise

BlockClosure >> newPromise
   "self is called with PromiseModel object.
   `aPromiseModel value: x` resolves the promise.
   `aPromiseModel signal: y` rejects the promise.
   Returns a Promise that eventually
   succeeds or fails, asynchronously."
   <return new JS_Promise(function (resolve, reject) {
     return self._value_($globals.PromiseModel._newResolve_reject_(
       resolve, reject
     ));
   })>

PromiseModel class >> newResolve: aBlock reject: anotherBlock
   "Creates a PromiseModel instance
   that calls aBlock when anInstance value: is sent
   and calls anotherBlock when anInstance signal: is sent."

PromiseModel >> value: anObject
   "Resolves underlying promise."

PromiseModel >> signal: anObject
   "Rejects underlying promise."

Collection >> allAsPromise
   "Returns a collection that succeeds
   when all promises in self asArray succeed.
   It is resolved with array of their results."
   ^ self asArray allAsPromise

Array >> allAsPromise
   <return JS_Promise.all(self._collect_(function (each) {
     return each._asPromise();
   }))>

Promise class >> all: aCollection
   "Returns a collection that succeeds
   when all promises in aCollection succeed.
   It is resolved with array of their results."
   ^ aCollection allAsPromise

Collection >> anyAsPromise
   "Returns a collection that succeeds
   when any of the promises in self asArray succeed.
   It is resolved with result of the first one that resolves."
   ^ self asArray anyAsPromise

Array >> anyAsPromise
   <return JS_Promise.any(self._collect_(function (each) {
     return each._asPromise();
   }))>

Promise class >> any: aCollection
   "Returns a collection that succeeds
   when any of the promises in aCollection succeed.
   It is resolved with result of the first one that resolves."
   ^ aCollection anyAsPromise

Promise >> all:then:on:do:
Promise >> all:then:catch:
Promise >> all:then:
Promise >> then:on:do:
Promise >> then:catch:
Promise >> then:
   "all: nadicBlock
   then: aBlockOrCollectionOfBlocks
   on: aClass do: catchBlock
   catch: catchAllBlock"
   <return self
     // if all: present
     .then(function(args) {
       return nadicBlock._valueWithPossibleArguments_(args);
     })
     // if then: arg is aBlock
     .then(function (result) {
       return aBlock._value_(result);
     })
     // if then: arg is collection of blocks,
     // thenArg do: [ :each | "add .then call as above" ]
     // if on: aClass do: catchBlock present
     .catch(function (ex) {
       if ($recv(ex)._isKindOf_(aClass)
         return catchBlock._value_(ex);
       else throw ex;
     })
     // if catch: catchAllBlock is present
     .catch(function (ex) {
       return catchAllBlock._value_(ex);
     })
   >

As you can see, Promise.reject API is not wrapped, but I don't it's
needed than often and can always be done with `[ :m | m signal: 'foo' ]
newPromise`.

For example:

   (fetch value: url)
     then: {
       [ :data | JSON parse: data].
       [ :parsed |
         parsed at: #id ifAbsent: [ Error signal: "Missing name."].
       parsed ].
       ...
     }
     catch: [ :e | self handleError: e ofUrl: url ]

and creating promise of your own:

   [ :model |
     anObject apiWithCallback: [ :err :result | err
       ifNotNil: [ model signal: err ]
       ifNil: [ console log: result. model value: result payload ] ]
   ] newPromise

Opinions?

Thanks, Herby

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

Herby Vojčík


Herby Vojčík wrote:

> Second version, after Philippe Back's suggestions:
>
> Object >> asPromise
> "returns a Promise resolved to self"
> <return JS_Promise.resolve(self)>
>
> JSObjectProxy >> asPromise
> <return JS_Promise.resolve(self["@jsObject"])>
>
> Promise class >> forBlock: aBlock
> "Inspired by GNU Smalltalk API.
> Returns a Promise fulfilled or rejected by `aBlock value`.
> Only usable for synchronous operations in aBlock."
> ^ aBlock valueAsPromise
>
> BlockClosure >> valueAsPromise
> <return JS_Promise.resolve().then(function () {return self._value()})>
>
> Promise class >> asyncForBlock: aBlock
> "aBlock is called with PromiseModel object.
> `aPromiseModel value: x` resolves the promise.
> `aPromiseModel signal: y` rejects the promise.
> Returns a Promise that eventually
> succeeds or fails, asynchronously."
> ^ aBlock newPromise
>
> BlockClosure >> newPromise
> "self is called with PromiseModel object.
> `aPromiseModel value: x` resolves the promise.
> `aPromiseModel signal: y` rejects the promise.
> Returns a Promise that eventually
> succeeds or fails, asynchronously."
> <return new JS_Promise(function (resolve, reject) {
> return self._value_($globals.PromiseModel._newResolve_reject_(
> resolve, reject
> ));
> })>
>
> PromiseModel class >> newResolve: aBlock reject: anotherBlock
> "Creates a PromiseModel instance
> that calls aBlock when anInstance value: is sent
> and calls anotherBlock when anInstance signal: is sent."
>
> PromiseModel >> value: anObject
> "Resolves underlying promise."
>
> PromiseModel >> signal: anObject
> "Rejects underlying promise."
>
> Collection >> allAsPromise
> "Returns a collection that succeeds
"Returns a promise that ...

> when all promises in self asArray succeed.
> It is resolved with array of their results."
> ^ self asArray allAsPromise
>
> Array >> allAsPromise
> <return JS_Promise.all(self._collect_(function (each) {
> return each._asPromise();
> }))>
>
> Promise class >> all: aCollection
> "Returns a collection that succeeds
"Returns a promise that ...
> when all promises in aCollection succeed.
> It is resolved with array of their results."
> ^ aCollection allAsPromise
>
> Collection >> anyAsPromise
> "Returns a collection that succeeds
"Returns a promise that ...

> when any of the promises in self asArray succeed.
> It is resolved with result of the first one that resolves."
> ^ self asArray anyAsPromise
>
> Array >> anyAsPromise
> <return JS_Promise.any(self._collect_(function (each) {
> return each._asPromise();
> }))>
>
> Promise class >> any: aCollection
> "Returns a collection that succeeds
"Returns a promise that ...

> when any of the promises in aCollection succeed.
> It is resolved with result of the first one that resolves."
> ^ aCollection anyAsPromise
>
> Promise >> all:then:on:do:
> Promise >> all:then:catch:
> Promise >> all:then:
> Promise >> then:on:do:
> Promise >> then:catch:
> Promise >> then:
> "all: nadicBlock
> then: aBlockOrCollectionOfBlocks
> on: aClass do: catchBlock
> catch: catchAllBlock"
> <return self
> // if all: present
> .then(function(args) {
> return nadicBlock._valueWithPossibleArguments_(args);
> })
> // if then: arg is aBlock
> .then(function (result) {
> return aBlock._value_(result);
> })
> // if then: arg is collection of blocks,
> // thenArg do: [ :each | "add .then call as above" ]
> // if on: aClass do: catchBlock present
> .catch(function (ex) {
> if ($recv(ex)._isKindOf_(aClass)
> return catchBlock._value_(ex);
> else throw ex;
> })
> // if catch: catchAllBlock is present
> .catch(function (ex) {
> return catchAllBlock._value_(ex);
> })
>  >
>
> As you can see, Promise.reject API is not wrapped, but I don't it's
> needed than often and can always be done with `[ :m | m signal: 'foo' ]
> newPromise`.
>
> For example:
>
> (fetch value: url)
> then: {
> [ :data | JSON parse: data].
> [ :parsed |
> parsed at: #id ifAbsent: [ Error signal: "Missing name."].
> parsed ].
> ...
> }
> catch: [ :e | self handleError: e ofUrl: url ]
>
> and creating promise of your own:
>
> [ :model |
> anObject apiWithCallback: [ :err :result | err
> ifNotNil: [ model signal: err ]
> ifNil: [ console log: result. model value: result payload ] ]
> ] newPromise
>
> Opinions?
>
> Thanks, Herby
>

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

Herby Vojčík
I see one more problem with this. As the ecosystem is now, there is not
just ES2015 Promise (native or polyfilled), that is, the global Promise,
but many frameworks use different implementations (Parse has its own
Parse.Promise, Angular has $q, there are Q and Bluebird implementations
which are also dependencies of some libraries).

So, Amber must actually be able to provide functionality for all major
implementations.

The way how to achieve it is, IMO, having AbstractPromise base class
with basic functionality, using only minimal subset (.then(null, fn)
instead of .catch(fn) etc.) and letting the rest on subclasses, while
providing subclass Promise that wraps global Promise expecting it to be
ES2015 one, but adding other AbstractPromise-derived wrappers for other
types of promises, installable as libraries.

Herby Vojčík wrote:

>
>
> Herby Vojčík wrote:
>> Second version, after Philippe Back's suggestions:
>>
>> Object >> asPromise
>> "returns a Promise resolved to self"
>> <return JS_Promise.resolve(self)>
>>
>> JSObjectProxy >> asPromise
>> <return JS_Promise.resolve(self["@jsObject"])>
>>
>> Promise class >> forBlock: aBlock
>> "Inspired by GNU Smalltalk API.
>> Returns a Promise fulfilled or rejected by `aBlock value`.
>> Only usable for synchronous operations in aBlock."
>> ^ aBlock valueAsPromise
>>
>> BlockClosure >> valueAsPromise
>> <return JS_Promise.resolve().then(function () {return self._value()})>
>>
>> Promise class >> asyncForBlock: aBlock
>> "aBlock is called with PromiseModel object.
>> `aPromiseModel value: x` resolves the promise.
>> `aPromiseModel signal: y` rejects the promise.
>> Returns a Promise that eventually
>> succeeds or fails, asynchronously."
>> ^ aBlock newPromise
>>
>> BlockClosure >> newPromise
>> "self is called with PromiseModel object.
>> `aPromiseModel value: x` resolves the promise.
>> `aPromiseModel signal: y` rejects the promise.
>> Returns a Promise that eventually
>> succeeds or fails, asynchronously."
>> <return new JS_Promise(function (resolve, reject) {
>> return self._value_($globals.PromiseModel._newResolve_reject_(
>> resolve, reject
>> ));
>> })>
>>
>> PromiseModel class >> newResolve: aBlock reject: anotherBlock
>> "Creates a PromiseModel instance
>> that calls aBlock when anInstance value: is sent
>> and calls anotherBlock when anInstance signal: is sent."
>>
>> PromiseModel >> value: anObject
>> "Resolves underlying promise."
>>
>> PromiseModel >> signal: anObject
>> "Rejects underlying promise."
>>
>> Collection >> allAsPromise
>> "Returns a collection that succeeds
> "Returns a promise that ...
>> when all promises in self asArray succeed.
>> It is resolved with array of their results."
>> ^ self asArray allAsPromise
>>
>> Array >> allAsPromise
>> <return JS_Promise.all(self._collect_(function (each) {
>> return each._asPromise();
>> }))>
>>
>> Promise class >> all: aCollection
>> "Returns a collection that succeeds
> "Returns a promise that ...
>> when all promises in aCollection succeed.
>> It is resolved with array of their results."
>> ^ aCollection allAsPromise
>>
>> Collection >> anyAsPromise
>> "Returns a collection that succeeds
> "Returns a promise that ...
>> when any of the promises in self asArray succeed.
>> It is resolved with result of the first one that resolves."
>> ^ self asArray anyAsPromise
>>
>> Array >> anyAsPromise
>> <return JS_Promise.any(self._collect_(function (each) {
>> return each._asPromise();
>> }))>
>>
>> Promise class >> any: aCollection
>> "Returns a collection that succeeds
> "Returns a promise that ...
>> when any of the promises in aCollection succeed.
>> It is resolved with result of the first one that resolves."
>> ^ aCollection anyAsPromise
>>
>> Promise >> all:then:on:do:
>> Promise >> all:then:catch:
>> Promise >> all:then:
>> Promise >> then:on:do:
>> Promise >> then:catch:
>> Promise >> then:
>> "all: nadicBlock
>> then: aBlockOrCollectionOfBlocks
>> on: aClass do: catchBlock
>> catch: catchAllBlock"
>> <return self
>> // if all: present
>> .then(function(args) {
>> return nadicBlock._valueWithPossibleArguments_(args);
>> })
>> // if then: arg is aBlock
>> .then(function (result) {
>> return aBlock._value_(result);
>> })
>> // if then: arg is collection of blocks,
>> // thenArg do: [ :each | "add .then call as above" ]
>> // if on: aClass do: catchBlock present
>> .catch(function (ex) {
>> if ($recv(ex)._isKindOf_(aClass)
>> return catchBlock._value_(ex);
>> else throw ex;
>> })
>> // if catch: catchAllBlock is present
>> .catch(function (ex) {
>> return catchAllBlock._value_(ex);
>> })
>> >
>>
>> As you can see, Promise.reject API is not wrapped, but I don't it's
>> needed than often and can always be done with `[ :m | m signal: 'foo' ]
>> newPromise`.
>>
>> For example:
>>
>> (fetch value: url)
>> then: {
>> [ :data | JSON parse: data].
>> [ :parsed |
>> parsed at: #id ifAbsent: [ Error signal: "Missing name."].
>> parsed ].
>> ...
>> }
>> catch: [ :e | self handleError: e ofUrl: url ]
>>
>> and creating promise of your own:
>>
>> [ :model |
>> anObject apiWithCallback: [ :err :result | err
>> ifNotNil: [ model signal: err ]
>> ifNil: [ console log: result. model value: result payload ] ]
>> ] newPromise
>>
>> Opinions?
>>
>> Thanks, Herby
>>
>

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

philippeback

Isn't is why Promise/A+ exists?

On Jan 28, 2016 4:57 PM, "Herby Vojčík" <[hidden email]> wrote:
I see one more problem with this. As the ecosystem is now, there is not just ES2015 Promise (native or polyfilled), that is, the global Promise, but many frameworks use different implementations (Parse has its own Parse.Promise, Angular has $q, there are Q and Bluebird implementations which are also dependencies of some libraries).

So, Amber must actually be able to provide functionality for all major implementations.

The way how to achieve it is, IMO, having AbstractPromise base class with basic functionality, using only minimal subset (.then(null, fn) instead of .catch(fn) etc.) and letting the rest on subclasses, while providing subclass Promise that wraps global Promise expecting it to be ES2015 one, but adding other AbstractPromise-derived wrappers for other types of promises, installable as libraries.

Herby Vojčík wrote:


Herby Vojčík wrote:
Second version, after Philippe Back's suggestions:

Object >> asPromise
"returns a Promise resolved to self"
<return JS_Promise.resolve(self)>

JSObjectProxy >> asPromise
<return JS_Promise.resolve(self["@jsObject"])>

Promise class >> forBlock: aBlock
"Inspired by GNU Smalltalk API.
Returns a Promise fulfilled or rejected by `aBlock value`.
Only usable for synchronous operations in aBlock."
^ aBlock valueAsPromise

BlockClosure >> valueAsPromise
<return JS_Promise.resolve().then(function () {return self._value()})>

Promise class >> asyncForBlock: aBlock
"aBlock is called with PromiseModel object.
`aPromiseModel value: x` resolves the promise.
`aPromiseModel signal: y` rejects the promise.
Returns a Promise that eventually
succeeds or fails, asynchronously."
^ aBlock newPromise

BlockClosure >> newPromise
"self is called with PromiseModel object.
`aPromiseModel value: x` resolves the promise.
`aPromiseModel signal: y` rejects the promise.
Returns a Promise that eventually
succeeds or fails, asynchronously."
<return new JS_Promise(function (resolve, reject) {
return self._value_($globals.PromiseModel._newResolve_reject_(
resolve, reject
));
})>

PromiseModel class >> newResolve: aBlock reject: anotherBlock
"Creates a PromiseModel instance
that calls aBlock when anInstance value: is sent
and calls anotherBlock when anInstance signal: is sent."

PromiseModel >> value: anObject
"Resolves underlying promise."

PromiseModel >> signal: anObject
"Rejects underlying promise."

Collection >> allAsPromise
"Returns a collection that succeeds
"Returns a promise that ...
when all promises in self asArray succeed.
It is resolved with array of their results."
^ self asArray allAsPromise

Array >> allAsPromise
<return JS_Promise.all(self._collect_(function (each) {
return each._asPromise();
}))>

Promise class >> all: aCollection
"Returns a collection that succeeds
"Returns a promise that ...
when all promises in aCollection succeed.
It is resolved with array of their results."
^ aCollection allAsPromise

Collection >> anyAsPromise
"Returns a collection that succeeds
"Returns a promise that ...
when any of the promises in self asArray succeed.
It is resolved with result of the first one that resolves."
^ self asArray anyAsPromise

Array >> anyAsPromise
<return JS_Promise.any(self._collect_(function (each) {
return each._asPromise();
}))>

Promise class >> any: aCollection
"Returns a collection that succeeds
"Returns a promise that ...
when any of the promises in aCollection succeed.
It is resolved with result of the first one that resolves."
^ aCollection anyAsPromise

Promise >> all:then:on:do:
Promise >> all:then:catch:
Promise >> all:then:
Promise >> then:on:do:
Promise >> then:catch:
Promise >> then:
"all: nadicBlock
then: aBlockOrCollectionOfBlocks
on: aClass do: catchBlock
catch: catchAllBlock"
<return self
// if all: present
.then(function(args) {
return nadicBlock._valueWithPossibleArguments_(args);
})
// if then: arg is aBlock
.then(function (result) {
return aBlock._value_(result);
})
// if then: arg is collection of blocks,
// thenArg do: [ :each | "add .then call as above" ]
// if on: aClass do: catchBlock present
.catch(function (ex) {
if ($recv(ex)._isKindOf_(aClass)
return catchBlock._value_(ex);
else throw ex;
})
// if catch: catchAllBlock is present
.catch(function (ex) {
return catchAllBlock._value_(ex);
})
>

As you can see, Promise.reject API is not wrapped, but I don't it's
needed than often and can always be done with `[ :m | m signal: 'foo' ]
newPromise`.

For example:

(fetch value: url)
then: {
[ :data | JSON parse: data].
[ :parsed |
parsed at: #id ifAbsent: [ Error signal: "Missing name."].
parsed ].
...
}
catch: [ :e | self handleError: e ofUrl: url ]

and creating promise of your own:

[ :model |
anObject apiWithCallback: [ :err :result | err
ifNotNil: [ model signal: err ]
ifNil: [ console log: result. model value: result payload ] ]
] newPromise

Opinions?

Thanks, Herby



--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

Herby Vojčík
One thing is API. There are insustry standards like Promise/A+ but they are minimal. Things like Promise.all or Promise.resolve differ beyween implementations. Some have finally, some have not.

(luckily, Parse was death-sentenced today by fb, so need not take it into account)

Second thing is, how Amber wraps native classes (combined with the fact that when you combine libs, you end up with different promise implementations side by side (and working together if only thing you take into account is .then)). Class wrapping in Amber is 1-to-1 correspondence. What means, if you have more implementations side by side, you need to wrap every one of them to have the transparency of just using then: family API for all of them. Hence, the idea of abstract base class with API and subclasses actually wrapping JS classes.

Dňa 28. januára 2016 19:15:06 CET používateľ "[hidden email]" <[hidden email]> napísal:

>Isn't is why Promise/A+ exists?
>On Jan 28, 2016 4:57 PM, "Herby Vojčík" <[hidden email]> wrote:
>
>> I see one more problem with this. As the ecosystem is now, there is
>not
>> just ES2015 Promise (native or polyfilled), that is, the global
>Promise,
>> but many frameworks use different implementations (Parse has its own
>> Parse.Promise, Angular has $q, there are Q and Bluebird
>implementations
>> which are also dependencies of some libraries).
>>
>> So, Amber must actually be able to provide functionality for all
>major
>> implementations.
>>
>> The way how to achieve it is, IMO, having AbstractPromise base class
>with
>> basic functionality, using only minimal subset (.then(null, fn)
>instead of
>> .catch(fn) etc.) and letting the rest on subclasses, while providing
>> subclass Promise that wraps global Promise expecting it to be ES2015
>one,
>> but adding other AbstractPromise-derived wrappers for other types of
>> promises, installable as libraries.
>>
>> Herby Vojčík wrote:
>>
>>>
>>>
>>> Herby Vojčík wrote:
>>>
>>>> Second version, after Philippe Back's suggestions:
>>>>
>>>> Object >> asPromise
>>>> "returns a Promise resolved to self"
>>>> <return JS_Promise.resolve(self)>
>>>>
>>>> JSObjectProxy >> asPromise
>>>> <return JS_Promise.resolve(self["@jsObject"])>
>>>>
>>>> Promise class >> forBlock: aBlock
>>>> "Inspired by GNU Smalltalk API.
>>>> Returns a Promise fulfilled or rejected by `aBlock value`.
>>>> Only usable for synchronous operations in aBlock."
>>>> ^ aBlock valueAsPromise
>>>>
>>>> BlockClosure >> valueAsPromise
>>>> <return JS_Promise.resolve().then(function () {return
>self._value()})>
>>>>
>>>> Promise class >> asyncForBlock: aBlock
>>>> "aBlock is called with PromiseModel object.
>>>> `aPromiseModel value: x` resolves the promise.
>>>> `aPromiseModel signal: y` rejects the promise.
>>>> Returns a Promise that eventually
>>>> succeeds or fails, asynchronously."
>>>> ^ aBlock newPromise
>>>>
>>>> BlockClosure >> newPromise
>>>> "self is called with PromiseModel object.
>>>> `aPromiseModel value: x` resolves the promise.
>>>> `aPromiseModel signal: y` rejects the promise.
>>>> Returns a Promise that eventually
>>>> succeeds or fails, asynchronously."
>>>> <return new JS_Promise(function (resolve, reject) {
>>>> return self._value_($globals.PromiseModel._newResolve_reject_(
>>>> resolve, reject
>>>> ));
>>>> })>
>>>>
>>>> PromiseModel class >> newResolve: aBlock reject: anotherBlock
>>>> "Creates a PromiseModel instance
>>>> that calls aBlock when anInstance value: is sent
>>>> and calls anotherBlock when anInstance signal: is sent."
>>>>
>>>> PromiseModel >> value: anObject
>>>> "Resolves underlying promise."
>>>>
>>>> PromiseModel >> signal: anObject
>>>> "Rejects underlying promise."
>>>>
>>>> Collection >> allAsPromise
>>>> "Returns a collection that succeeds
>>>>
>>> "Returns a promise that ...
>>>
>>>> when all promises in self asArray succeed.
>>>> It is resolved with array of their results."
>>>> ^ self asArray allAsPromise
>>>>
>>>> Array >> allAsPromise
>>>> <return JS_Promise.all(self._collect_(function (each) {
>>>> return each._asPromise();
>>>> }))>
>>>>
>>>> Promise class >> all: aCollection
>>>> "Returns a collection that succeeds
>>>>
>>> "Returns a promise that ...
>>>
>>>> when all promises in aCollection succeed.
>>>> It is resolved with array of their results."
>>>> ^ aCollection allAsPromise
>>>>
>>>> Collection >> anyAsPromise
>>>> "Returns a collection that succeeds
>>>>
>>> "Returns a promise that ...
>>>
>>>> when any of the promises in self asArray succeed.
>>>> It is resolved with result of the first one that resolves."
>>>> ^ self asArray anyAsPromise
>>>>
>>>> Array >> anyAsPromise
>>>> <return JS_Promise.any(self._collect_(function (each) {
>>>> return each._asPromise();
>>>> }))>
>>>>
>>>> Promise class >> any: aCollection
>>>> "Returns a collection that succeeds
>>>>
>>> "Returns a promise that ...
>>>
>>>> when any of the promises in aCollection succeed.
>>>> It is resolved with result of the first one that resolves."
>>>> ^ aCollection anyAsPromise
>>>>
>>>> Promise >> all:then:on:do:
>>>> Promise >> all:then:catch:
>>>> Promise >> all:then:
>>>> Promise >> then:on:do:
>>>> Promise >> then:catch:
>>>> Promise >> then:
>>>> "all: nadicBlock
>>>> then: aBlockOrCollectionOfBlocks
>>>> on: aClass do: catchBlock
>>>> catch: catchAllBlock"
>>>> <return self
>>>> // if all: present
>>>> .then(function(args) {
>>>> return nadicBlock._valueWithPossibleArguments_(args);
>>>> })
>>>> // if then: arg is aBlock
>>>> .then(function (result) {
>>>> return aBlock._value_(result);
>>>> })
>>>> // if then: arg is collection of blocks,
>>>> // thenArg do: [ :each | "add .then call as above" ]
>>>> // if on: aClass do: catchBlock present
>>>> .catch(function (ex) {
>>>> if ($recv(ex)._isKindOf_(aClass)
>>>> return catchBlock._value_(ex);
>>>> else throw ex;
>>>> })
>>>> // if catch: catchAllBlock is present
>>>> .catch(function (ex) {
>>>> return catchAllBlock._value_(ex);
>>>> })
>>>> >
>>>>
>>>> As you can see, Promise.reject API is not wrapped, but I don't it's
>>>> needed than often and can always be done with `[ :m | m signal:
>'foo' ]
>>>> newPromise`.
>>>>
>>>> For example:
>>>>
>>>> (fetch value: url)
>>>> then: {
>>>> [ :data | JSON parse: data].
>>>> [ :parsed |
>>>> parsed at: #id ifAbsent: [ Error signal: "Missing name."].
>>>> parsed ].
>>>> ...
>>>> }
>>>> catch: [ :e | self handleError: e ofUrl: url ]
>>>>
>>>> and creating promise of your own:
>>>>
>>>> [ :model |
>>>> anObject apiWithCallback: [ :err :result | err
>>>> ifNotNil: [ model signal: err ]
>>>> ifNil: [ console log: result. model value: result payload ] ]
>>>> ] newPromise
>>>>
>>>> Opinions?
>>>>
>>>> Thanks, Herby
>>>>
>>>>
>>>
>> --
>> You received this message because you are subscribed to the Google
>Groups
>> "amber-lang" group.
>> To unsubscribe from this group and stop receiving emails from it,
>send an
>> email to [hidden email].
>> For more options, visit https://groups.google.com/d/optout.
>>

--
Odoslané z môjho Android zariadenia pomocou K-9 Mail.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

Herby Vojčík
In reply to this post by Herby Vojčík
There is one more problem as I see it: there surely will be promises
created as inline objects, as here:
https://github.com/requirejs/alameda-prim/commit/9ad546fff02b765ebe6943929218d07c1f2c655d#diff-aeb8d45ae32f6d853d4ac616eb5875b9R138.
This means, we must actually implement all:then:on:do:catch: family of
messages in JSObjectProxy as well, and catch all thenables dynamically.

What also means, there is enough to wrap the official global Promise,
let the others be caught by JSObjectProxy, and people can wrap other
implementations if they see it fit / it helps them with performance
issues / it allows them create promises of a specific library.

So I would say, create a base class called Thenable with *then:* family.
Then, subclass Promise wrapping global ES2015 Promise and also *then:*
family in JSObjectProxy which can test dynamically if typeof self.then
=== "function" && (self.catch == null || typeof self.catch ===
"function") and if yes, delegate to Thenable to do the work on self.

Herby

Herby Vojčík wrote:

> I see one more problem with this. As the ecosystem is now, there is not
> just ES2015 Promise (native or polyfilled), that is, the global Promise,
> but many frameworks use different implementations (Parse has its own
> Parse.Promise, Angular has $q, there are Q and Bluebird implementations
> which are also dependencies of some libraries).
>
> So, Amber must actually be able to provide functionality for all major
> implementations.
>
> The way how to achieve it is, IMO, having AbstractPromise base class
> with basic functionality, using only minimal subset (.then(null, fn)
> instead of .catch(fn) etc.) and letting the rest on subclasses, while
> providing subclass Promise that wraps global Promise expecting it to be
> ES2015 one, but adding other AbstractPromise-derived wrappers for other
> types of promises, installable as libraries.
>
> Herby Vojčík wrote:
>>
>>
>> Herby Vojčík wrote:
>>> Second version, after Philippe Back's suggestions:
>>>
>>> Object >> asPromise
>>> "returns a Promise resolved to self"
>>> <return JS_Promise.resolve(self)>
>>>
>>> JSObjectProxy >> asPromise
>>> <return JS_Promise.resolve(self["@jsObject"])>
>>>
>>> Promise class >> forBlock: aBlock
>>> "Inspired by GNU Smalltalk API.
>>> Returns a Promise fulfilled or rejected by `aBlock value`.
>>> Only usable for synchronous operations in aBlock."
>>> ^ aBlock valueAsPromise
>>>
>>> BlockClosure >> valueAsPromise
>>> <return JS_Promise.resolve().then(function () {return self._value()})>
>>>
>>> Promise class >> asyncForBlock: aBlock
>>> "aBlock is called with PromiseModel object.
>>> `aPromiseModel value: x` resolves the promise.
>>> `aPromiseModel signal: y` rejects the promise.
>>> Returns a Promise that eventually
>>> succeeds or fails, asynchronously."
>>> ^ aBlock newPromise
>>>
>>> BlockClosure >> newPromise
>>> "self is called with PromiseModel object.
>>> `aPromiseModel value: x` resolves the promise.
>>> `aPromiseModel signal: y` rejects the promise.
>>> Returns a Promise that eventually
>>> succeeds or fails, asynchronously."
>>> <return new JS_Promise(function (resolve, reject) {
>>> return self._value_($globals.PromiseModel._newResolve_reject_(
>>> resolve, reject
>>> ));
>>> })>
>>>
>>> PromiseModel class >> newResolve: aBlock reject: anotherBlock
>>> "Creates a PromiseModel instance
>>> that calls aBlock when anInstance value: is sent
>>> and calls anotherBlock when anInstance signal: is sent."
>>>
>>> PromiseModel >> value: anObject
>>> "Resolves underlying promise."
>>>
>>> PromiseModel >> signal: anObject
>>> "Rejects underlying promise."
>>>
>>> Collection >> allAsPromise
>>> "Returns a collection that succeeds
>> "Returns a promise that ...
>>> when all promises in self asArray succeed.
>>> It is resolved with array of their results."
>>> ^ self asArray allAsPromise
>>>
>>> Array >> allAsPromise
>>> <return JS_Promise.all(self._collect_(function (each) {
>>> return each._asPromise();
>>> }))>
>>>
>>> Promise class >> all: aCollection
>>> "Returns a collection that succeeds
>> "Returns a promise that ...
>>> when all promises in aCollection succeed.
>>> It is resolved with array of their results."
>>> ^ aCollection allAsPromise
>>>
>>> Collection >> anyAsPromise
>>> "Returns a collection that succeeds
>> "Returns a promise that ...
>>> when any of the promises in self asArray succeed.
>>> It is resolved with result of the first one that resolves."
>>> ^ self asArray anyAsPromise
>>>
>>> Array >> anyAsPromise
>>> <return JS_Promise.any(self._collect_(function (each) {
>>> return each._asPromise();
>>> }))>
>>>
>>> Promise class >> any: aCollection
>>> "Returns a collection that succeeds
>> "Returns a promise that ...
>>> when any of the promises in aCollection succeed.
>>> It is resolved with result of the first one that resolves."
>>> ^ aCollection anyAsPromise
>>>
>>> Promise >> all:then:on:do:
>>> Promise >> all:then:catch:
>>> Promise >> all:then:
>>> Promise >> then:on:do:
>>> Promise >> then:catch:
>>> Promise >> then:
>>> "all: nadicBlock
>>> then: aBlockOrCollectionOfBlocks
>>> on: aClass do: catchBlock
>>> catch: catchAllBlock"
>>> <return self
>>> // if all: present
>>> .then(function(args) {
>>> return nadicBlock._valueWithPossibleArguments_(args);
>>> })
>>> // if then: arg is aBlock
>>> .then(function (result) {
>>> return aBlock._value_(result);
>>> })
>>> // if then: arg is collection of blocks,
>>> // thenArg do: [ :each | "add .then call as above" ]
>>> // if on: aClass do: catchBlock present
>>> .catch(function (ex) {
>>> if ($recv(ex)._isKindOf_(aClass)
>>> return catchBlock._value_(ex);
>>> else throw ex;
>>> })
>>> // if catch: catchAllBlock is present
>>> .catch(function (ex) {
>>> return catchAllBlock._value_(ex);
>>> })
>>> >
>>>
>>> As you can see, Promise.reject API is not wrapped, but I don't it's
>>> needed than often and can always be done with `[ :m | m signal: 'foo' ]
>>> newPromise`.
>>>
>>> For example:
>>>
>>> (fetch value: url)
>>> then: {
>>> [ :data | JSON parse: data].
>>> [ :parsed |
>>> parsed at: #id ifAbsent: [ Error signal: "Missing name."].
>>> parsed ].
>>> ...
>>> }
>>> catch: [ :e | self handleError: e ofUrl: url ]
>>>
>>> and creating promise of your own:
>>>
>>> [ :model |
>>> anObject apiWithCallback: [ :err :result | err
>>> ifNotNil: [ model signal: err ]
>>> ifNil: [ console log: result. model value: result payload ] ]
>>> ] newPromise
>>>
>>> Opinions?
>>>
>>> Thanks, Herby
>>>
>>
>

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

philippeback

Ok I see.

We need a couple of real examples (like in the Async Javascript book) I think, so that we can explore a couple cases properly.

Phil

On Jan 29, 2016 10:24 PM, "Herby Vojčík" <[hidden email]> wrote:
There is one more problem as I see it: there surely will be promises created as inline objects, as here: https://github.com/requirejs/alameda-prim/commit/9ad546fff02b765ebe6943929218d07c1f2c655d#diff-aeb8d45ae32f6d853d4ac616eb5875b9R138. This means, we must actually implement all:then:on:do:catch: family of messages in JSObjectProxy as well, and catch all thenables dynamically.

What also means, there is enough to wrap the official global Promise, let the others be caught by JSObjectProxy, and people can wrap other implementations if they see it fit / it helps them with performance issues / it allows them create promises of a specific library.

So I would say, create a base class called Thenable with *then:* family. Then, subclass Promise wrapping global ES2015 Promise and also *then:* family in JSObjectProxy which can test dynamically if typeof self.then === "function" && (self.catch == null || typeof self.catch === "function") and if yes, delegate to Thenable to do the work on self.

Herby

Herby Vojčík wrote:
I see one more problem with this. As the ecosystem is now, there is not
just ES2015 Promise (native or polyfilled), that is, the global Promise,
but many frameworks use different implementations (Parse has its own
Parse.Promise, Angular has $q, there are Q and Bluebird implementations
which are also dependencies of some libraries).

So, Amber must actually be able to provide functionality for all major
implementations.

The way how to achieve it is, IMO, having AbstractPromise base class
with basic functionality, using only minimal subset (.then(null, fn)
instead of .catch(fn) etc.) and letting the rest on subclasses, while
providing subclass Promise that wraps global Promise expecting it to be
ES2015 one, but adding other AbstractPromise-derived wrappers for other
types of promises, installable as libraries.

Herby Vojčík wrote:


Herby Vojčík wrote:
Second version, after Philippe Back's suggestions:

Object >> asPromise
"returns a Promise resolved to self"
<return JS_Promise.resolve(self)>

JSObjectProxy >> asPromise
<return JS_Promise.resolve(self["@jsObject"])>

Promise class >> forBlock: aBlock
"Inspired by GNU Smalltalk API.
Returns a Promise fulfilled or rejected by `aBlock value`.
Only usable for synchronous operations in aBlock."
^ aBlock valueAsPromise

BlockClosure >> valueAsPromise
<return JS_Promise.resolve().then(function () {return self._value()})>

Promise class >> asyncForBlock: aBlock
"aBlock is called with PromiseModel object.
`aPromiseModel value: x` resolves the promise.
`aPromiseModel signal: y` rejects the promise.
Returns a Promise that eventually
succeeds or fails, asynchronously."
^ aBlock newPromise

BlockClosure >> newPromise
"self is called with PromiseModel object.
`aPromiseModel value: x` resolves the promise.
`aPromiseModel signal: y` rejects the promise.
Returns a Promise that eventually
succeeds or fails, asynchronously."
<return new JS_Promise(function (resolve, reject) {
return self._value_($globals.PromiseModel._newResolve_reject_(
resolve, reject
));
})>

PromiseModel class >> newResolve: aBlock reject: anotherBlock
"Creates a PromiseModel instance
that calls aBlock when anInstance value: is sent
and calls anotherBlock when anInstance signal: is sent."

PromiseModel >> value: anObject
"Resolves underlying promise."

PromiseModel >> signal: anObject
"Rejects underlying promise."

Collection >> allAsPromise
"Returns a collection that succeeds
"Returns a promise that ...
when all promises in self asArray succeed.
It is resolved with array of their results."
^ self asArray allAsPromise

Array >> allAsPromise
<return JS_Promise.all(self._collect_(function (each) {
return each._asPromise();
}))>

Promise class >> all: aCollection
"Returns a collection that succeeds
"Returns a promise that ...
when all promises in aCollection succeed.
It is resolved with array of their results."
^ aCollection allAsPromise

Collection >> anyAsPromise
"Returns a collection that succeeds
"Returns a promise that ...
when any of the promises in self asArray succeed.
It is resolved with result of the first one that resolves."
^ self asArray anyAsPromise

Array >> anyAsPromise
<return JS_Promise.any(self._collect_(function (each) {
return each._asPromise();
}))>

Promise class >> any: aCollection
"Returns a collection that succeeds
"Returns a promise that ...
when any of the promises in aCollection succeed.
It is resolved with result of the first one that resolves."
^ aCollection anyAsPromise

Promise >> all:then:on:do:
Promise >> all:then:catch:
Promise >> all:then:
Promise >> then:on:do:
Promise >> then:catch:
Promise >> then:
"all: nadicBlock
then: aBlockOrCollectionOfBlocks
on: aClass do: catchBlock
catch: catchAllBlock"
<return self
// if all: present
.then(function(args) {
return nadicBlock._valueWithPossibleArguments_(args);
})
// if then: arg is aBlock
.then(function (result) {
return aBlock._value_(result);
})
// if then: arg is collection of blocks,
// thenArg do: [ :each | "add .then call as above" ]
// if on: aClass do: catchBlock present
.catch(function (ex) {
if ($recv(ex)._isKindOf_(aClass)
return catchBlock._value_(ex);
else throw ex;
})
// if catch: catchAllBlock is present
.catch(function (ex) {
return catchAllBlock._value_(ex);
})
>

As you can see, Promise.reject API is not wrapped, but I don't it's
needed than often and can always be done with `[ :m | m signal: 'foo' ]
newPromise`.

For example:

(fetch value: url)
then: {
[ :data | JSON parse: data].
[ :parsed |
parsed at: #id ifAbsent: [ Error signal: "Missing name."].
parsed ].
...
}
catch: [ :e | self handleError: e ofUrl: url ]

and creating promise of your own:

[ :model |
anObject apiWithCallback: [ :err :result | err
ifNotNil: [ model signal: err ]
ifNil: [ console log: result. model value: result payload ] ]
] newPromise

Opinions?

Thanks, Herby




--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

Herby Vojčík


[hidden email] wrote:
> Ok I see.
>
> We need a couple of real examples (like in the Async Javascript book) I
> think, so that we can explore a couple cases properly.
>
> Phil

I in fact think that implementing just the bare minimum I envisioned is
doable fast (in fact only thing needed is to do the JSObjectProxy part,
but I would include the Thenable class there as well, so the delegation
is put up properly). Creating the promises is then still hard because it
must use JS API which is less Smalltalk-like ('reject value: x' is not
as English-sentence-like like 'model signal: x'), but the main part will
be there.

I am just a bit tired to do it just now, but I can probably do it
somewhere in the weekend, and even release it. I want to add more
promises to startup process, mainly, making amber.initialize({...})
return a promise, but for that would need more work with polyfilling for
non-Promise environments.

>
> On Jan 29, 2016 10:24 PM, "Herby Vojčík" <[hidden email]
> <mailto:[hidden email]>> wrote:
>
>     There is one more problem as I see it: there surely will be promises
>     created as inline objects, as here:
>     https://github.com/requirejs/alameda-prim/commit/9ad546fff02b765ebe6943929218d07c1f2c655d#diff-aeb8d45ae32f6d853d4ac616eb5875b9R138.
>     This means, we must actually implement all:then:on:do:catch: family
>     of messages in JSObjectProxy as well, and catch all thenables
>     dynamically.
>
>     What also means, there is enough to wrap the official global
>     Promise, let the others be caught by JSObjectProxy, and people can
>     wrap other implementations if they see it fit / it helps them with
>     performance issues / it allows them create promises of a specific
>     library.
>
>     So I would say, create a base class called Thenable with *then:*
>     family. Then, subclass Promise wrapping global ES2015 Promise and
>     also *then:* family in JSObjectProxy which can test dynamically if
>     typeof self.then === "function" && (self.catch == null || typeof
>     self.catch === "function") and if yes, delegate to Thenable to do
>     the work on self.
>
>     Herby
>
>     Herby Vojčík wrote:
>
>         I see one more problem with this. As the ecosystem is now, there
>         is not
>         just ES2015 Promise (native or polyfilled), that is, the global
>         Promise,
>         but many frameworks use different implementations (Parse has its own
>         Parse.Promise, Angular has $q, there are Q and Bluebird
>         implementations
>         which are also dependencies of some libraries).
>
>         So, Amber must actually be able to provide functionality for all
>         major
>         implementations.
>
>         The way how to achieve it is, IMO, having AbstractPromise base class
>         with basic functionality, using only minimal subset (.then(null, fn)
>         instead of .catch(fn) etc.) and letting the rest on subclasses,
>         while
>         providing subclass Promise that wraps global Promise expecting
>         it to be
>         ES2015 one, but adding other AbstractPromise-derived wrappers
>         for other
>         types of promises, installable as libraries.
>
>         Herby Vojčík wrote:
>
>
>
>             Herby Vojčík wrote:
>
>                 Second version, after Philippe Back's suggestions:
>
>                 Object >> asPromise
>                 "returns a Promise resolved to self"
>                 <return JS_Promise.resolve(self)>
>
>                 JSObjectProxy >> asPromise
>                 <return JS_Promise.resolve(self["@jsObject"])>
>
>                 Promise class >> forBlock: aBlock
>                 "Inspired by GNU Smalltalk API.
>                 Returns a Promise fulfilled or rejected by `aBlock value`.
>                 Only usable for synchronous operations in aBlock."
>                 ^ aBlock valueAsPromise
>
>                 BlockClosure >> valueAsPromise
>                 <return JS_Promise.resolve().then(function () {return
>                 self._value()})>
>
>                 Promise class >> asyncForBlock: aBlock
>                 "aBlock is called with PromiseModel object.
>                 `aPromiseModel value: x` resolves the promise.
>                 `aPromiseModel signal: y` rejects the promise.
>                 Returns a Promise that eventually
>                 succeeds or fails, asynchronously."
>                 ^ aBlock newPromise
>
>                 BlockClosure >> newPromise
>                 "self is called with PromiseModel object.
>                 `aPromiseModel value: x` resolves the promise.
>                 `aPromiseModel signal: y` rejects the promise.
>                 Returns a Promise that eventually
>                 succeeds or fails, asynchronously."
>                 <return new JS_Promise(function (resolve, reject) {
>                 return
>                 self._value_($globals.PromiseModel._newResolve_reject_(
>                 resolve, reject
>                 ));
>                 })>
>
>                 PromiseModel class >> newResolve: aBlock reject:
>                 anotherBlock
>                 "Creates a PromiseModel instance
>                 that calls aBlock when anInstance value: is sent
>                 and calls anotherBlock when anInstance signal: is sent."
>
>                 PromiseModel >> value: anObject
>                 "Resolves underlying promise."
>
>                 PromiseModel >> signal: anObject
>                 "Rejects underlying promise."
>
>                 Collection >> allAsPromise
>                 "Returns a collection that succeeds
>
>             "Returns a promise that ...
>
>                 when all promises in self asArray succeed.
>                 It is resolved with array of their results."
>                 ^ self asArray allAsPromise
>
>                 Array >> allAsPromise
>                 <return JS_Promise.all(self._collect_(function (each) {
>                 return each._asPromise();
>                 }))>
>
>                 Promise class >> all: aCollection
>                 "Returns a collection that succeeds
>
>             "Returns a promise that ...
>
>                 when all promises in aCollection succeed.
>                 It is resolved with array of their results."
>                 ^ aCollection allAsPromise
>
>                 Collection >> anyAsPromise
>                 "Returns a collection that succeeds
>
>             "Returns a promise that ...
>
>                 when any of the promises in self asArray succeed.
>                 It is resolved with result of the first one that resolves."
>                 ^ self asArray anyAsPromise
>
>                 Array >> anyAsPromise
>                 <return JS_Promise.any(self._collect_(function (each) {
>                 return each._asPromise();
>                 }))>
>
>                 Promise class >> any: aCollection
>                 "Returns a collection that succeeds
>
>             "Returns a promise that ...
>
>                 when any of the promises in aCollection succeed.
>                 It is resolved with result of the first one that resolves."
>                 ^ aCollection anyAsPromise
>
>                 Promise >> all:then:on:do:
>                 Promise >> all:then:catch:
>                 Promise >> all:then:
>                 Promise >> then:on:do:
>                 Promise >> then:catch:
>                 Promise >> then:
>                 "all: nadicBlock
>                 then: aBlockOrCollectionOfBlocks
>                 on: aClass do: catchBlock
>                 catch: catchAllBlock"
>                 <return self
>                 // if all: present
>                 .then(function(args) {
>                 return nadicBlock._valueWithPossibleArguments_(args);
>                 })
>                 // if then: arg is aBlock
>                 .then(function (result) {
>                 return aBlock._value_(result);
>                 })
>                 // if then: arg is collection of blocks,
>                 // thenArg do: [ :each | "add .then call as above" ]
>                 // if on: aClass do: catchBlock present
>                 .catch(function (ex) {
>                 if ($recv(ex)._isKindOf_(aClass)
>                 return catchBlock._value_(ex);
>                 else throw ex;
>                 })
>                 // if catch: catchAllBlock is present
>                 .catch(function (ex) {
>                 return catchAllBlock._value_(ex);
>                 })
>                  >
>
>                 As you can see, Promise.reject API is not wrapped, but I
>                 don't it's
>                 needed than often and can always be done with `[ :m | m
>                 signal: 'foo' ]
>                 newPromise`.
>
>                 For example:
>
>                 (fetch value: url)
>                 then: {
>                 [ :data | JSON parse: data].
>                 [ :parsed |
>                 parsed at: #id ifAbsent: [ Error signal: "Missing name."].
>                 parsed ].
>                 ...
>                 }
>                 catch: [ :e | self handleError: e ofUrl: url ]
>
>                 and creating promise of your own:
>
>                 [ :model |
>                 anObject apiWithCallback: [ :err :result | err
>                 ifNotNil: [ model signal: err ]
>                 ifNil: [ console log: result. model value: result
>                 payload ] ]
>                 ] newPromise
>
>                 Opinions?
>
>                 Thanks, Herby
>
>
>
>
>     --
>     You received this message because you are subscribed to the Google
>     Groups "amber-lang" group.
>     To unsubscribe from this group and stop receiving emails from it,
>     send an email to [hidden email]
>     <mailto:amber-lang%[hidden email]>.
>     For more options, visit https://groups.google.com/d/optout.
>
> --
> You received this message because you are subscribed to the Google
> Groups "amber-lang" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to [hidden email]
> <mailto:[hidden email]>.
> For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

philippe.back@highoctane.be

Sweet. Take care, I'll follow along.
Maybe we can do a new podcast episode about current Amber and promises.

Phil

On Jan 29, 2016 11:31 PM, "Herby Vojčík" <[hidden email]> wrote:


[hidden email] wrote:
Ok I see.

We need a couple of real examples (like in the Async Javascript book) I
think, so that we can explore a couple cases properly.

Phil

I in fact think that implementing just the bare minimum I envisioned is doable fast (in fact only thing needed is to do the JSObjectProxy part, but I would include the Thenable class there as well, so the delegation is put up properly). Creating the promises is then still hard because it must use JS API which is less Smalltalk-like ('reject value: x' is not as English-sentence-like like 'model signal: x'), but the main part will be there.

I am just a bit tired to do it just now, but I can probably do it somewhere in the weekend, and even release it. I want to add more promises to startup process, mainly, making amber.initialize({...}) return a promise, but for that would need more work with polyfilling for non-Promise environments.


On Jan 29, 2016 10:24 PM, "Herby Vojčík" <[hidden email]
<mailto:[hidden email]>> wrote:

    There is one more problem as I see it: there surely will be promises
    created as inline objects, as here:
    https://github.com/requirejs/alameda-prim/commit/9ad546fff02b765ebe6943929218d07c1f2c655d#diff-aeb8d45ae32f6d853d4ac616eb5875b9R138.
    This means, we must actually implement all:then:on:do:catch: family
    of messages in JSObjectProxy as well, and catch all thenables
    dynamically.

    What also means, there is enough to wrap the official global
    Promise, let the others be caught by JSObjectProxy, and people can
    wrap other implementations if they see it fit / it helps them with
    performance issues / it allows them create promises of a specific
    library.

    So I would say, create a base class called Thenable with *then:*
    family. Then, subclass Promise wrapping global ES2015 Promise and
    also *then:* family in JSObjectProxy which can test dynamically if
    typeof self.then === "function" && (self.catch == null || typeof
    self.catch === "function") and if yes, delegate to Thenable to do
    the work on self.

    Herby

    Herby Vojčík wrote:

        I see one more problem with this. As the ecosystem is now, there
        is not
        just ES2015 Promise (native or polyfilled), that is, the global
        Promise,
        but many frameworks use different implementations (Parse has its own
        Parse.Promise, Angular has $q, there are Q and Bluebird
        implementations
        which are also dependencies of some libraries).

        So, Amber must actually be able to provide functionality for all
        major
        implementations.

        The way how to achieve it is, IMO, having AbstractPromise base class
        with basic functionality, using only minimal subset (.then(null, fn)
        instead of .catch(fn) etc.) and letting the rest on subclasses,
        while
        providing subclass Promise that wraps global Promise expecting
        it to be
        ES2015 one, but adding other AbstractPromise-derived wrappers
        for other
        types of promises, installable as libraries.

        Herby Vojčík wrote:



            Herby Vojčík wrote:

                Second version, after Philippe Back's suggestions:

                Object >> asPromise
                "returns a Promise resolved to self"
                <return JS_Promise.resolve(self)>

                JSObjectProxy >> asPromise
                <return JS_Promise.resolve(self["@jsObject"])>

                Promise class >> forBlock: aBlock
                "Inspired by GNU Smalltalk API.
                Returns a Promise fulfilled or rejected by `aBlock value`.
                Only usable for synchronous operations in aBlock."
                ^ aBlock valueAsPromise

                BlockClosure >> valueAsPromise
                <return JS_Promise.resolve().then(function () {return
                self._value()})>

                Promise class >> asyncForBlock: aBlock
                "aBlock is called with PromiseModel object.
                `aPromiseModel value: x` resolves the promise.
                `aPromiseModel signal: y` rejects the promise.
                Returns a Promise that eventually
                succeeds or fails, asynchronously."
                ^ aBlock newPromise

                BlockClosure >> newPromise
                "self is called with PromiseModel object.
                `aPromiseModel value: x` resolves the promise.
                `aPromiseModel signal: y` rejects the promise.
                Returns a Promise that eventually
                succeeds or fails, asynchronously."
                <return new JS_Promise(function (resolve, reject) {
                return
                self._value_($globals.PromiseModel._newResolve_reject_(
                resolve, reject
                ));
                })>

                PromiseModel class >> newResolve: aBlock reject:
                anotherBlock
                "Creates a PromiseModel instance
                that calls aBlock when anInstance value: is sent
                and calls anotherBlock when anInstance signal: is sent."

                PromiseModel >> value: anObject
                "Resolves underlying promise."

                PromiseModel >> signal: anObject
                "Rejects underlying promise."

                Collection >> allAsPromise
                "Returns a collection that succeeds

            "Returns a promise that ...

                when all promises in self asArray succeed.
                It is resolved with array of their results."
                ^ self asArray allAsPromise

                Array >> allAsPromise
                <return JS_Promise.all(self._collect_(function (each) {
                return each._asPromise();
                }))>

                Promise class >> all: aCollection
                "Returns a collection that succeeds

            "Returns a promise that ...

                when all promises in aCollection succeed.
                It is resolved with array of their results."
                ^ aCollection allAsPromise

                Collection >> anyAsPromise
                "Returns a collection that succeeds

            "Returns a promise that ...

                when any of the promises in self asArray succeed.
                It is resolved with result of the first one that resolves."
                ^ self asArray anyAsPromise

                Array >> anyAsPromise
                <return JS_Promise.any(self._collect_(function (each) {
                return each._asPromise();
                }))>

                Promise class >> any: aCollection
                "Returns a collection that succeeds

            "Returns a promise that ...

                when any of the promises in aCollection succeed.
                It is resolved with result of the first one that resolves."
                ^ aCollection anyAsPromise

                Promise >> all:then:on:do:
                Promise >> all:then:catch:
                Promise >> all:then:
                Promise >> then:on:do:
                Promise >> then:catch:
                Promise >> then:
                "all: nadicBlock
                then: aBlockOrCollectionOfBlocks
                on: aClass do: catchBlock
                catch: catchAllBlock"
                <return self
                // if all: present
                .then(function(args) {
                return nadicBlock._valueWithPossibleArguments_(args);
                })
                // if then: arg is aBlock
                .then(function (result) {
                return aBlock._value_(result);
                })
                // if then: arg is collection of blocks,
                // thenArg do: [ :each | "add .then call as above" ]
                // if on: aClass do: catchBlock present
                .catch(function (ex) {
                if ($recv(ex)._isKindOf_(aClass)
                return catchBlock._value_(ex);
                else throw ex;
                })
                // if catch: catchAllBlock is present
                .catch(function (ex) {
                return catchAllBlock._value_(ex);
                })
                 >

                As you can see, Promise.reject API is not wrapped, but I
                don't it's
                needed than often and can always be done with `[ :m | m
                signal: 'foo' ]
                newPromise`.

                For example:

                (fetch value: url)
                then: {
                [ :data | JSON parse: data].
                [ :parsed |
                parsed at: #id ifAbsent: [ Error signal: "Missing name."].
                parsed ].
                ...
                }
                catch: [ :e | self handleError: e ofUrl: url ]

                and creating promise of your own:

                [ :model |
                anObject apiWithCallback: [ :err :result | err
                ifNotNil: [ model signal: err ]
                ifNil: [ console log: result. model value: result
                payload ] ]
                ] newPromise

                Opinions?

                Thanks, Herby




    --
    You received this message because you are subscribed to the Google
    Groups "amber-lang" group.
    To unsubscribe from this group and stop receiving emails from it,
    send an email to [hidden email]
    <mailto:[hidden email]>.
    For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [hidden email]
<mailto:[hidden email]>.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

Herby Vojčík
I am thinking about removing all: and giving then: blocks ability to
take array elements. A result would always go to first argument, but if
the block would have more arguments in its definition and the result is
an array, first n-1 elements of an array would be passed to additional
arguments. Is it too crazy (if one wants to eschew that, one can always
one one-arg block and do things on its own)?

Herby

[hidden email] wrote:

> Sweet. Take care, I'll follow along.
> Maybe we can do a new podcast episode about current Amber and promises.
>
> Phil
>
> On Jan 29, 2016 11:31 PM, "Herby Vojčík" <[hidden email]
> <mailto:[hidden email]>> wrote:
>
>
>
>     [hidden email] <mailto:[hidden email]> wrote:
>
>         Ok I see.
>
>         We need a couple of real examples (like in the Async Javascript
>         book) I
>         think, so that we can explore a couple cases properly.
>
>         Phil
>
>
>     I in fact think that implementing just the bare minimum I envisioned
>     is doable fast (in fact only thing needed is to do the JSObjectProxy
>     part, but I would include the Thenable class there as well, so the
>     delegation is put up properly). Creating the promises is then still
>     hard because it must use JS API which is less Smalltalk-like
>     ('reject value: x' is not as English-sentence-like like 'model
>     signal: x'), but the main part will be there.
>
>     I am just a bit tired to do it just now, but I can probably do it
>     somewhere in the weekend, and even release it. I want to add more
>     promises to startup process, mainly, making amber.initialize({...})
>     return a promise, but for that would need more work with polyfilling
>     for non-Promise environments.
>
>
>         On Jan 29, 2016 10:24 PM, "Herby Vojčík" <[hidden email]
>         <mailto:[hidden email]>
>         <mailto:[hidden email] <mailto:[hidden email]>>> wrote:
>
>              There is one more problem as I see it: there surely will be
>         promises
>              created as inline objects, as here:
>         https://github.com/requirejs/alameda-prim/commit/9ad546fff02b765ebe6943929218d07c1f2c655d#diff-aeb8d45ae32f6d853d4ac616eb5875b9R138.
>              This means, we must actually implement
>         all:then:on:do:catch: family
>              of messages in JSObjectProxy as well, and catch all thenables
>              dynamically.
>
>              What also means, there is enough to wrap the official global
>              Promise, let the others be caught by JSObjectProxy, and
>         people can
>              wrap other implementations if they see it fit / it helps
>         them with
>              performance issues / it allows them create promises of a
>         specific
>              library.
>
>              So I would say, create a base class called Thenable with
>         *then:*
>              family. Then, subclass Promise wrapping global ES2015
>         Promise and
>              also *then:* family in JSObjectProxy which can test
>         dynamically if
>              typeof self.then === "function" && (self.catch == null ||
>         typeof
>              self.catch === "function") and if yes, delegate to Thenable
>         to do
>              the work on self.
>
>              Herby
>
>              Herby Vojčík wrote:
>
>                  I see one more problem with this. As the ecosystem is
>         now, there
>                  is not
>                  just ES2015 Promise (native or polyfilled), that is,
>         the global
>                  Promise,
>                  but many frameworks use different implementations
>         (Parse has its own
>                  Parse.Promise, Angular has $q, there are Q and Bluebird
>                  implementations
>                  which are also dependencies of some libraries).
>
>                  So, Amber must actually be able to provide
>         functionality for all
>                  major
>                  implementations.
>
>                  The way how to achieve it is, IMO, having
>         AbstractPromise base class
>                  with basic functionality, using only minimal subset
>         (.then(null, fn)
>                  instead of .catch(fn) etc.) and letting the rest on
>         subclasses,
>                  while
>                  providing subclass Promise that wraps global Promise
>         expecting
>                  it to be
>                  ES2015 one, but adding other AbstractPromise-derived
>         wrappers
>                  for other
>                  types of promises, installable as libraries.
>
>                  Herby Vojčík wrote:
>
>
>
>                      Herby Vojčík wrote:
>
>                          Second version, after Philippe Back's suggestions:
>
>                          Object >> asPromise
>         "returns a Promise resolved to self"
>         <return JS_Promise.resolve(self)>
>
>                          JSObjectProxy >> asPromise
>         <return JS_Promise.resolve(self["@jsObject"])>
>
>                          Promise class >> forBlock: aBlock
>         "Inspired by GNU Smalltalk API.
>                          Returns a Promise fulfilled or rejected by
>         `aBlock value`.
>                          Only usable for synchronous operations in aBlock."
>                          ^ aBlock valueAsPromise
>
>                          BlockClosure >> valueAsPromise
>         <return JS_Promise.resolve().then(function () {return
>                          self._value()})>
>
>                          Promise class >> asyncForBlock: aBlock
>         "aBlock is called with PromiseModel object.
>                          `aPromiseModel value: x` resolves the promise.
>                          `aPromiseModel signal: y` rejects the promise.
>                          Returns a Promise that eventually
>                          succeeds or fails, asynchronously."
>                          ^ aBlock newPromise
>
>                          BlockClosure >> newPromise
>         "self is called with PromiseModel object.
>                          `aPromiseModel value: x` resolves the promise.
>                          `aPromiseModel signal: y` rejects the promise.
>                          Returns a Promise that eventually
>                          succeeds or fails, asynchronously."
>         <return new JS_Promise(function (resolve, reject) {
>                          return
>
>         self._value_($globals.PromiseModel._newResolve_reject_(
>                          resolve, reject
>                          ));
>                          })>
>
>                          PromiseModel class >> newResolve: aBlock reject:
>                          anotherBlock
>         "Creates a PromiseModel instance
>                          that calls aBlock when anInstance value: is sent
>                          and calls anotherBlock when anInstance signal:
>         is sent."
>
>                          PromiseModel >> value: anObject
>         "Resolves underlying promise."
>
>                          PromiseModel >> signal: anObject
>         "Rejects underlying promise."
>
>                          Collection >> allAsPromise
>         "Returns a collection that succeeds
>
>         "Returns a promise that ...
>
>                          when all promises in self asArray succeed.
>                          It is resolved with array of their results."
>                          ^ self asArray allAsPromise
>
>                          Array >> allAsPromise
>         <return JS_Promise.all(self._collect_(function (each) {
>                          return each._asPromise();
>                          }))>
>
>                          Promise class >> all: aCollection
>         "Returns a collection that succeeds
>
>         "Returns a promise that ...
>
>                          when all promises in aCollection succeed.
>                          It is resolved with array of their results."
>                          ^ aCollection allAsPromise
>
>                          Collection >> anyAsPromise
>         "Returns a collection that succeeds
>
>         "Returns a promise that ...
>
>                          when any of the promises in self asArray succeed.
>                          It is resolved with result of the first one
>         that resolves."
>                          ^ self asArray anyAsPromise
>
>                          Array >> anyAsPromise
>         <return JS_Promise.any(self._collect_(function (each) {
>                          return each._asPromise();
>                          }))>
>
>                          Promise class >> any: aCollection
>         "Returns a collection that succeeds
>
>         "Returns a promise that ...
>
>                          when any of the promises in aCollection succeed.
>                          It is resolved with result of the first one
>         that resolves."
>                          ^ aCollection anyAsPromise
>
>                          Promise >> all:then:on:do:
>                          Promise >> all:then:catch:
>                          Promise >> all:then:
>                          Promise >> then:on:do:
>                          Promise >> then:catch:
>                          Promise >> then:
>         "all: nadicBlock
>                          then: aBlockOrCollectionOfBlocks
>                          on: aClass do: catchBlock
>                          catch: catchAllBlock"
>         <return self
>                          // if all: present
>                          .then(function(args) {
>                          return
>         nadicBlock._valueWithPossibleArguments_(args);
>                          })
>                          // if then: arg is aBlock
>                          .then(function (result) {
>                          return aBlock._value_(result);
>                          })
>                          // if then: arg is collection of blocks,
>                          // thenArg do: [ :each | "add .then call as
>         above" ]
>                          // if on: aClass do: catchBlock present
>                          .catch(function (ex) {
>                          if ($recv(ex)._isKindOf_(aClass)
>                          return catchBlock._value_(ex);
>                          else throw ex;
>                          })
>                          // if catch: catchAllBlock is present
>                          .catch(function (ex) {
>                          return catchAllBlock._value_(ex);
>                          })
>          >
>
>                          As you can see, Promise.reject API is not
>         wrapped, but I
>                          don't it's
>                          needed than often and can always be done with
>         `[ :m | m
>                          signal: 'foo' ]
>                          newPromise`.
>
>                          For example:
>
>                          (fetch value: url)
>                          then: {
>                          [ :data | JSON parse: data].
>                          [ :parsed |
>                          parsed at: #id ifAbsent: [ Error signal:
>         "Missing name."].
>                          parsed ].
>                          ...
>                          }
>                          catch: [ :e | self handleError: e ofUrl: url ]
>
>                          and creating promise of your own:
>
>                          [ :model |
>                          anObject apiWithCallback: [ :err :result | err
>                          ifNotNil: [ model signal: err ]
>                          ifNil: [ console log: result. model value: result
>                          payload ] ]
>                          ] newPromise
>
>                          Opinions?
>
>                          Thanks, Herby
>
>
>
>
>              --
>              You received this message because you are subscribed to the
>         Google
>              Groups "amber-lang" group.
>              To unsubscribe from this group and stop receiving emails
>         from it,
>              send an email to [hidden email]
>         <mailto:amber-lang%[hidden email]>
>         <mailto:amber-lang%[hidden email]
>         <mailto:amber-lang%[hidden email]>>.
>              For more options, visit https://groups.google.com/d/optout.
>
>         --
>         You received this message because you are subscribed to the Google
>         Groups "amber-lang" group.
>         To unsubscribe from this group and stop receiving emails from
>         it, send
>         an email to [hidden email]
>         <mailto:amber-lang%[hidden email]>
>         <mailto:[hidden email]
>         <mailto:amber-lang%[hidden email]>>.
>         For more options, visit https://groups.google.com/d/optout.
>
>
>     --
>     You received this message because you are subscribed to the Google
>     Groups "amber-lang" group.
>     To unsubscribe from this group and stop receiving emails from it,
>     send an email to [hidden email]
>     <mailto:amber-lang%[hidden email]>.
>     For more options, visit https://groups.google.com/d/optout.
>
> --
> You received this message because you are subscribed to the Google
> Groups "amber-lang" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to [hidden email]
> <mailto:[hidden email]>.
> For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

philippeback
Looks okay.

On Mon, Feb 1, 2016 at 11:21 PM, Herby Vojčík <[hidden email]> wrote:
I am thinking about removing all: and giving then: blocks ability to take array elements. A result would always go to first argument, but if the block would have more arguments in its definition and the result is an array, first n-1 elements of an array would be passed to additional arguments. Is it too crazy (if one wants to eschew that, one can always one one-arg block and do things on its own)?

Herby

[hidden email] wrote:
Sweet. Take care, I'll follow along.
Maybe we can do a new podcast episode about current Amber and promises.

Phil

On Jan 29, 2016 11:31 PM, "Herby Vojčík" <[hidden email]
<mailto:[hidden email]>> wrote:



    [hidden email] <mailto:[hidden email]> wrote:

        Ok I see.

        We need a couple of real examples (like in the Async Javascript
        book) I
        think, so that we can explore a couple cases properly.

        Phil


    I in fact think that implementing just the bare minimum I envisioned
    is doable fast (in fact only thing needed is to do the JSObjectProxy
    part, but I would include the Thenable class there as well, so the
    delegation is put up properly). Creating the promises is then still
    hard because it must use JS API which is less Smalltalk-like
    ('reject value: x' is not as English-sentence-like like 'model
    signal: x'), but the main part will be there.

    I am just a bit tired to do it just now, but I can probably do it
    somewhere in the weekend, and even release it. I want to add more
    promises to startup process, mainly, making amber.initialize({...})
    return a promise, but for that would need more work with polyfilling
    for non-Promise environments.


        On Jan 29, 2016 10:24 PM, "Herby Vojčík" <[hidden email]
        <mailto:[hidden email]>
        <mailto:[hidden email] <mailto:[hidden email]>>> wrote:

             There is one more problem as I see it: there surely will be
        promises
             created as inline objects, as here:
        https://github.com/requirejs/alameda-prim/commit/9ad546fff02b765ebe6943929218d07c1f2c655d#diff-aeb8d45ae32f6d853d4ac616eb5875b9R138.
             This means, we must actually implement
        all:then:on:do:catch: family
             of messages in JSObjectProxy as well, and catch all thenables
             dynamically.

             What also means, there is enough to wrap the official global
             Promise, let the others be caught by JSObjectProxy, and
        people can
             wrap other implementations if they see it fit / it helps
        them with
             performance issues / it allows them create promises of a
        specific
             library.

             So I would say, create a base class called Thenable with
        *then:*
             family. Then, subclass Promise wrapping global ES2015
        Promise and
             also *then:* family in JSObjectProxy which can test
        dynamically if
             typeof self.then === "function" && (self.catch == null ||
        typeof
             self.catch === "function") and if yes, delegate to Thenable
        to do
             the work on self.

             Herby

             Herby Vojčík wrote:

                 I see one more problem with this. As the ecosystem is
        now, there
                 is not
                 just ES2015 Promise (native or polyfilled), that is,
        the global
                 Promise,
                 but many frameworks use different implementations
        (Parse has its own
                 Parse.Promise, Angular has $q, there are Q and Bluebird
                 implementations
                 which are also dependencies of some libraries).

                 So, Amber must actually be able to provide
        functionality for all
                 major
                 implementations.

                 The way how to achieve it is, IMO, having
        AbstractPromise base class
                 with basic functionality, using only minimal subset
        (.then(null, fn)
                 instead of .catch(fn) etc.) and letting the rest on
        subclasses,
                 while
                 providing subclass Promise that wraps global Promise
        expecting
                 it to be
                 ES2015 one, but adding other AbstractPromise-derived
        wrappers
                 for other
                 types of promises, installable as libraries.

                 Herby Vojčík wrote:



                     Herby Vojčík wrote:

                         Second version, after Philippe Back's suggestions:

                         Object >> asPromise
        "returns a Promise resolved to self"
        <return JS_Promise.resolve(self)>

                         JSObjectProxy >> asPromise
        <return JS_Promise.resolve(self["@jsObject"])>

                         Promise class >> forBlock: aBlock
        "Inspired by GNU Smalltalk API.
                         Returns a Promise fulfilled or rejected by
        `aBlock value`.
                         Only usable for synchronous operations in aBlock."
                         ^ aBlock valueAsPromise

                         BlockClosure >> valueAsPromise
        <return JS_Promise.resolve().then(function () {return
                         self._value()})>

                         Promise class >> asyncForBlock: aBlock
        "aBlock is called with PromiseModel object.
                         `aPromiseModel value: x` resolves the promise.
                         `aPromiseModel signal: y` rejects the promise.
                         Returns a Promise that eventually
                         succeeds or fails, asynchronously."
                         ^ aBlock newPromise

                         BlockClosure >> newPromise
        "self is called with PromiseModel object.
                         `aPromiseModel value: x` resolves the promise.
                         `aPromiseModel signal: y` rejects the promise.
                         Returns a Promise that eventually
                         succeeds or fails, asynchronously."
        <return new JS_Promise(function (resolve, reject) {
                         return

        self._value_($globals.PromiseModel._newResolve_reject_(
                         resolve, reject
                         ));
                         })>

                         PromiseModel class >> newResolve: aBlock reject:
                         anotherBlock
        "Creates a PromiseModel instance
                         that calls aBlock when anInstance value: is sent
                         and calls anotherBlock when anInstance signal:
        is sent."

                         PromiseModel >> value: anObject
        "Resolves underlying promise."

                         PromiseModel >> signal: anObject
        "Rejects underlying promise."

                         Collection >> allAsPromise
        "Returns a collection that succeeds

        "Returns a promise that ...

                         when all promises in self asArray succeed.
                         It is resolved with array of their results."
                         ^ self asArray allAsPromise

                         Array >> allAsPromise
        <return JS_Promise.all(self._collect_(function (each) {
                         return each._asPromise();
                         }))>

                         Promise class >> all: aCollection
        "Returns a collection that succeeds

        "Returns a promise that ...

                         when all promises in aCollection succeed.
                         It is resolved with array of their results."
                         ^ aCollection allAsPromise

                         Collection >> anyAsPromise
        "Returns a collection that succeeds

        "Returns a promise that ...

                         when any of the promises in self asArray succeed.
                         It is resolved with result of the first one
        that resolves."
                         ^ self asArray anyAsPromise

                         Array >> anyAsPromise
        <return JS_Promise.any(self._collect_(function (each) {
                         return each._asPromise();
                         }))>

                         Promise class >> any: aCollection
        "Returns a collection that succeeds

        "Returns a promise that ...

                         when any of the promises in aCollection succeed.
                         It is resolved with result of the first one
        that resolves."
                         ^ aCollection anyAsPromise

                         Promise >> all:then:on:do:
                         Promise >> all:then:catch:
                         Promise >> all:then:
                         Promise >> then:on:do:
                         Promise >> then:catch:
                         Promise >> then:
        "all: nadicBlock
                         then: aBlockOrCollectionOfBlocks
                         on: aClass do: catchBlock
                         catch: catchAllBlock"
        <return self
                         // if all: present
                         .then(function(args) {
                         return
        nadicBlock._valueWithPossibleArguments_(args);
                         })
                         // if then: arg is aBlock
                         .then(function (result) {
                         return aBlock._value_(result);
                         })
                         // if then: arg is collection of blocks,
                         // thenArg do: [ :each | "add .then call as
        above" ]
                         // if on: aClass do: catchBlock present
                         .catch(function (ex) {
                         if ($recv(ex)._isKindOf_(aClass)
                         return catchBlock._value_(ex);
                         else throw ex;
                         })
                         // if catch: catchAllBlock is present
                         .catch(function (ex) {
                         return catchAllBlock._value_(ex);
                         })
         >

                         As you can see, Promise.reject API is not
        wrapped, but I
                         don't it's
                         needed than often and can always be done with
        `[ :m | m
                         signal: 'foo' ]
                         newPromise`.

                         For example:

                         (fetch value: url)
                         then: {
                         [ :data | JSON parse: data].
                         [ :parsed |
                         parsed at: #id ifAbsent: [ Error signal:
        "Missing name."].
                         parsed ].
                         ...
                         }
                         catch: [ :e | self handleError: e ofUrl: url ]

                         and creating promise of your own:

                         [ :model |
                         anObject apiWithCallback: [ :err :result | err
                         ifNotNil: [ model signal: err ]
                         ifNil: [ console log: result. model value: result
                         payload ] ]
                         ] newPromise

                         Opinions?

                         Thanks, Herby




             --
             You received this message because you are subscribed to the
        Google
             Groups "amber-lang" group.
             To unsubscribe from this group and stop receiving emails
        from it,
             send an email to [hidden email]
        <mailto:[hidden email]>
        <mailto:[hidden email]
        <mailto:[hidden email]>>.
             For more options, visit https://groups.google.com/d/optout.

        --
        You received this message because you are subscribed to the Google
        Groups "amber-lang" group.
        To unsubscribe from this group and stop receiving emails from
        it, send
        an email to [hidden email]
        <mailto:[hidden email]>
        <mailto:[hidden email]
        <mailto:[hidden email]>>.
        For more options, visit https://groups.google.com/d/optout.


    --
    You received this message because you are subscribed to the Google
    Groups "amber-lang" group.
    To unsubscribe from this group and stop receiving emails from it,
    send an email to [hidden email]
    <mailto:[hidden email]>.
    For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [hidden email]
<mailto:[hidden email]>.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Promise API

Herby Vojčík
A little problem during implementation.

When I do 'PP=Promise' in JS console (so as to have access to JS
Promise) and do

   (JSObjectProxy on: PP) resolve
     then: [ :x | Error signal: 'intentional' ]
     catch: [ :err | Transcript show: err; cr ]

then error is shown in the Transcript, but before that it is caught by
debugger. Some change allowing "unsafe" run of ST code from JS must be
done to boot.js for this to work as expected. :-(

[hidden email] wrote:

> Looks okay.
>
> On Mon, Feb 1, 2016 at 11:21 PM, Herby Vojčík <[hidden email]
> <mailto:[hidden email]>> wrote:
>
>     I am thinking about removing all: and giving then: blocks ability to
>     take array elements. A result would always go to first argument, but
>     if the block would have more arguments in its definition and the
>     result is an array, first n-1 elements of an array would be passed
>     to additional arguments. Is it too crazy (if one wants to eschew
>     that, one can always one one-arg block and do things on its own)?
>
>     Herby
>
>     [hidden email] <mailto:[hidden email]> wrote:
>
>         Sweet. Take care, I'll follow along.
>         Maybe we can do a new podcast episode about current Amber and
>         promises.
>
>         Phil
>
>         On Jan 29, 2016 11:31 PM, "Herby Vojčík" <[hidden email]
>         <mailto:[hidden email]>
>         <mailto:[hidden email] <mailto:[hidden email]>>> wrote:
>
>
>
>         [hidden email] <mailto:[hidden email]>
>         <mailto:[hidden email] <mailto:[hidden email]>> wrote:
>
>                  Ok I see.
>
>                  We need a couple of real examples (like in the Async
>         Javascript
>                  book) I
>                  think, so that we can explore a couple cases properly.
>
>                  Phil
>
>
>              I in fact think that implementing just the bare minimum I
>         envisioned
>              is doable fast (in fact only thing needed is to do the
>         JSObjectProxy
>              part, but I would include the Thenable class there as well,
>         so the
>              delegation is put up properly). Creating the promises is
>         then still
>              hard because it must use JS API which is less Smalltalk-like
>              ('reject value: x' is not as English-sentence-like like 'model
>              signal: x'), but the main part will be there.
>
>              I am just a bit tired to do it just now, but I can probably
>         do it
>              somewhere in the weekend, and even release it. I want to
>         add more
>              promises to startup process, mainly, making
>         amber.initialize({...})
>              return a promise, but for that would need more work with
>         polyfilling
>              for non-Promise environments.
>
>
>                  On Jan 29, 2016 10:24 PM, "Herby Vojčík"
>         <[hidden email] <mailto:[hidden email]>
>         <mailto:[hidden email] <mailto:[hidden email]>>
>         <mailto:[hidden email] <mailto:[hidden email]>
>         <mailto:[hidden email] <mailto:[hidden email]>>>> wrote:
>
>                       There is one more problem as I see it: there
>         surely will be
>                  promises
>                       created as inline objects, as here:
>         https://github.com/requirejs/alameda-prim/commit/9ad546fff02b765ebe6943929218d07c1f2c655d#diff-aeb8d45ae32f6d853d4ac616eb5875b9R138.
>                       This means, we must actually implement
>                  all:then:on:do:catch: family
>                       of messages in JSObjectProxy as well, and catch
>         all thenables
>                       dynamically.
>
>                       What also means, there is enough to wrap the
>         official global
>                       Promise, let the others be caught by
>         JSObjectProxy, and
>                  people can
>                       wrap other implementations if they see it fit / it
>         helps
>                  them with
>                       performance issues / it allows them create
>         promises of a
>                  specific
>                       library.
>
>                       So I would say, create a base class called
>         Thenable with
>                  *then:*
>                       family. Then, subclass Promise wrapping global ES2015
>                  Promise and
>                       also *then:* family in JSObjectProxy which can test
>                  dynamically if
>                       typeof self.then === "function" && (self.catch ==
>         null ||
>                  typeof
>                       self.catch === "function") and if yes, delegate to
>         Thenable
>                  to do
>                       the work on self.
>
>                       Herby
>
>                       Herby Vojčík wrote:
>
>                           I see one more problem with this. As the
>         ecosystem is
>                  now, there
>                           is not
>                           just ES2015 Promise (native or polyfilled),
>         that is,
>                  the global
>                           Promise,
>                           but many frameworks use different implementations
>                  (Parse has its own
>                           Parse.Promise, Angular has $q, there are Q and
>         Bluebird
>                           implementations
>                           which are also dependencies of some libraries).
>
>                           So, Amber must actually be able to provide
>                  functionality for all
>                           major
>                           implementations.
>
>                           The way how to achieve it is, IMO, having
>                  AbstractPromise base class
>                           with basic functionality, using only minimal
>         subset
>                  (.then(null, fn)
>                           instead of .catch(fn) etc.) and letting the
>         rest on
>                  subclasses,
>                           while
>                           providing subclass Promise that wraps global
>         Promise
>                  expecting
>                           it to be
>                           ES2015 one, but adding other
>         AbstractPromise-derived
>                  wrappers
>                           for other
>                           types of promises, installable as libraries.
>
>                           Herby Vojčík wrote:
>
>
>
>                               Herby Vojčík wrote:
>
>                                   Second version, after Philippe Back's
>         suggestions:
>
>                                   Object >> asPromise
>         "returns a Promise resolved to self"
>         <return JS_Promise.resolve(self)>
>
>                                   JSObjectProxy >> asPromise
>         <return JS_Promise.resolve(self["@jsObject"])>
>
>                                   Promise class >> forBlock: aBlock
>         "Inspired by GNU Smalltalk API.
>                                   Returns a Promise fulfilled or rejected by
>                  `aBlock value`.
>                                   Only usable for synchronous operations
>         in aBlock."
>                                   ^ aBlock valueAsPromise
>
>                                   BlockClosure >> valueAsPromise
>         <return JS_Promise.resolve().then(function () {return
>                                   self._value()})>
>
>                                   Promise class >> asyncForBlock: aBlock
>         "aBlock is called with PromiseModel object.
>                                   `aPromiseModel value: x` resolves the
>         promise.
>                                   `aPromiseModel signal: y` rejects the
>         promise.
>                                   Returns a Promise that eventually
>                                   succeeds or fails, asynchronously."
>                                   ^ aBlock newPromise
>
>                                   BlockClosure >> newPromise
>         "self is called with PromiseModel object.
>                                   `aPromiseModel value: x` resolves the
>         promise.
>                                   `aPromiseModel signal: y` rejects the
>         promise.
>                                   Returns a Promise that eventually
>                                   succeeds or fails, asynchronously."
>         <return new JS_Promise(function (resolve, reject) {
>                                   return
>
>                  self._value_($globals.PromiseModel._newResolve_reject_(
>                                   resolve, reject
>                                   ));
>                                   })>
>
>                                   PromiseModel class >> newResolve:
>         aBlock reject:
>                                   anotherBlock
>         "Creates a PromiseModel instance
>                                   that calls aBlock when anInstance
>         value: is sent
>                                   and calls anotherBlock when anInstance
>         signal:
>                  is sent."
>
>                                   PromiseModel >> value: anObject
>         "Resolves underlying promise."
>
>                                   PromiseModel >> signal: anObject
>         "Rejects underlying promise."
>
>                                   Collection >> allAsPromise
>         "Returns a collection that succeeds
>
>         "Returns a promise that ...
>
>                                   when all promises in self asArray succeed.
>                                   It is resolved with array of their
>         results."
>                                   ^ self asArray allAsPromise
>
>                                   Array >> allAsPromise
>         <return JS_Promise.all(self._collect_(function (each) {
>                                   return each._asPromise();
>                                   }))>
>
>                                   Promise class >> all: aCollection
>         "Returns a collection that succeeds
>
>         "Returns a promise that ...
>
>                                   when all promises in aCollection succeed.
>                                   It is resolved with array of their
>         results."
>                                   ^ aCollection allAsPromise
>
>                                   Collection >> anyAsPromise
>         "Returns a collection that succeeds
>
>         "Returns a promise that ...
>
>                                   when any of the promises in self
>         asArray succeed.
>                                   It is resolved with result of the
>         first one
>                  that resolves."
>                                   ^ self asArray anyAsPromise
>
>                                   Array >> anyAsPromise
>         <return JS_Promise.any(self._collect_(function (each) {
>                                   return each._asPromise();
>                                   }))>
>
>                                   Promise class >> any: aCollection
>         "Returns a collection that succeeds
>
>         "Returns a promise that ...
>
>                                   when any of the promises in
>         aCollection succeed.
>                                   It is resolved with result of the
>         first one
>                  that resolves."
>                                   ^ aCollection anyAsPromise
>
>                                   Promise >> all:then:on:do:
>                                   Promise >> all:then:catch:
>                                   Promise >> all:then:
>                                   Promise >> then:on:do:
>                                   Promise >> then:catch:
>                                   Promise >> then:
>         "all: nadicBlock
>                                   then: aBlockOrCollectionOfBlocks
>                                   on: aClass do: catchBlock
>                                   catch: catchAllBlock"
>         <return self
>                                   // if all: present
>                                   .then(function(args) {
>                                   return
>                  nadicBlock._valueWithPossibleArguments_(args);
>                                   })
>                                   // if then: arg is aBlock
>                                   .then(function (result) {
>                                   return aBlock._value_(result);
>                                   })
>                                   // if then: arg is collection of blocks,
>                                   // thenArg do: [ :each | "add .then
>         call as
>                  above" ]
>                                   // if on: aClass do: catchBlock present
>                                   .catch(function (ex) {
>                                   if ($recv(ex)._isKindOf_(aClass)
>                                   return catchBlock._value_(ex);
>                                   else throw ex;
>                                   })
>                                   // if catch: catchAllBlock is present
>                                   .catch(function (ex) {
>                                   return catchAllBlock._value_(ex);
>                                   })
>          >
>
>                                   As you can see, Promise.reject API is not
>                  wrapped, but I
>                                   don't it's
>                                   needed than often and can always be
>         done with
>                  `[ :m | m
>                                   signal: 'foo' ]
>                                   newPromise`.
>
>                                   For example:
>
>                                   (fetch value: url)
>                                   then: {
>                                   [ :data | JSON parse: data].
>                                   [ :parsed |
>                                   parsed at: #id ifAbsent: [ Error signal:
>         "Missing name."].
>                                   parsed ].
>                                   ...
>                                   }
>                                   catch: [ :e | self handleError: e
>         ofUrl: url ]
>
>                                   and creating promise of your own:
>
>                                   [ :model |
>                                   anObject apiWithCallback: [ :err
>         :result | err
>                                   ifNotNil: [ model signal: err ]
>                                   ifNil: [ console log: result. model
>         value: result
>                                   payload ] ]
>                                   ] newPromise
>
>                                   Opinions?
>
>                                   Thanks, Herby
>
>
>
>
>                       --
>                       You received this message because you are
>         subscribed to the
>                  Google
>                       Groups "amber-lang" group.
>                       To unsubscribe from this group and stop receiving
>         emails
>                  from it,
>                       send an email to
>         [hidden email]
>         <mailto:amber-lang%[hidden email]>
>         <mailto:amber-lang%[hidden email]
>         <mailto:amber-lang%[hidden email]>>
>         <mailto:amber-lang%[hidden email]
>         <mailto:amber-lang%[hidden email]>
>         <mailto:amber-lang%[hidden email]
>         <mailto:amber-lang%[hidden email]>>>.
>                       For more options, visit
>         https://groups.google.com/d/optout.
>
>                  --
>                  You received this message because you are subscribed to
>         the Google
>                  Groups "amber-lang" group.
>                  To unsubscribe from this group and stop receiving
>         emails from
>                  it, send
>                  an email to [hidden email]
>         <mailto:amber-lang%[hidden email]>
>         <mailto:amber-lang%[hidden email]
>         <mailto:amber-lang%[hidden email]>>
>         <mailto:[hidden email]
>         <mailto:amber-lang%[hidden email]>
>         <mailto:amber-lang%[hidden email]
>         <mailto:amber-lang%[hidden email]>>>.
>                  For more options, visit https://groups.google.com/d/optout.
>
>
>              --
>              You received this message because you are subscribed to the
>         Google
>              Groups "amber-lang" group.
>              To unsubscribe from this group and stop receiving emails
>         from it,
>              send an email to [hidden email]
>         <mailto:amber-lang%[hidden email]>
>         <mailto:amber-lang%[hidden email]
>         <mailto:amber-lang%[hidden email]>>.
>              For more options, visit https://groups.google.com/d/optout.
>
>         --
>         You received this message because you are subscribed to the Google
>         Groups "amber-lang" group.
>         To unsubscribe from this group and stop receiving emails from
>         it, send
>         an email to [hidden email]
>         <mailto:amber-lang%[hidden email]>
>         <mailto:[hidden email]
>         <mailto:amber-lang%[hidden email]>>.
>         For more options, visit https://groups.google.com/d/optout.
>
>
>     --
>     You received this message because you are subscribed to the Google
>     Groups "amber-lang" group.
>     To unsubscribe from this group and stop receiving emails from it,
>     send an email to [hidden email]
>     <mailto:amber-lang%[hidden email]>.
>     For more options, visit https://groups.google.com/d/optout.
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "amber-lang" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to [hidden email]
> <mailto:[hidden email]>.
> For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "amber-lang" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
For more options, visit https://groups.google.com/d/optout.
12