refactoring promise iteration...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2022-12-07 02:21:06 +03:00
parent 62c00a9fd4
commit 14dfeab599
2 changed files with 130 additions and 24 deletions

View File

@ -70,12 +70,14 @@ function(name){
return this.constructor(
this.then(function(lst){
return lst[name](...args) })) } }
var promiseProxy =
module.promiseProxy =
function(name){
return async function(...args){
return (await this)[name](...args) } }
var IterablePromise =
module.IterablePromise =
object.Constructor('IterablePromise', Promise, {
@ -149,7 +151,19 @@ object.Constructor('IterablePromise', Promise, {
var pack = function(){
return [list].flat()
[map](function(elem){
// XXX EXPEREMENTAL...
return elem instanceof IterablePromise ?
(elem.isSync() ?
handler(elem.sync())
// XXX need to handle this but keep it IterablePromise...
: elem.iterthen(handler))
: (elem instanceof SyncPromise
&& !(elem.sync() instanceof Promise)) ?
handler(elem.sync())
: elem && elem.then ?
/*/
return elem && elem.then ?
//*/
(stoppable ?
// stoppable -- need to handle stop async...
elem
@ -238,6 +252,35 @@ object.Constructor('IterablePromise', Promise, {
return elem })
: [handler(elem)] })
.flat() },
// XXX EXPEREMENTAL...
// XXX this should return IterablePromise if .__packed is partially sync (???)
// unpack array (sync/async)...
__unpack: function(list){
list = list
?? this.__packed
// handle promise list...
if(list instanceof IterablePromise){
return list.__unpack() }
if(list instanceof Promise){
return list
.then(this.__unpack.bind(this)) }
var res = []
for(var e of list){
if(e instanceof IterablePromise){
e = e.__unpack() }
if(e instanceof SyncPromise){
e = e.sync() }
// give up on a sync solution...
if(e instanceof Promise){
// XXX can we return an IterablePromise???
// XXX this will cause infinite recursion....
//return Promise.iter(list).flat() }
return Promise.all(list)
.then(function(list){
return list.flat() }) }
res.push(e) }
return res.flat() },
/*/
// unpack array (async)...
__unpack: async function(list){
list = list
@ -248,11 +291,12 @@ object.Constructor('IterablePromise', Promise, {
// do the work...
: (await Promise.all(list))
.flat() },
//*/
[Symbol.asyncIterator]: async function*(){
var list = this.__packed
if(list instanceof Promise){
yield this.__unpack(await list)
yield* await this.__unpack(list)
return }
for await(var elem of list){
yield* elem instanceof Array ?
@ -510,7 +554,10 @@ object.Constructor('IterablePromise', Promise, {
// NOTE: this is slightly different from .then(..) in that it can be
// called without arguments and return a promise wrapper. This can
// be useful to hide special promise functionality...
then: function (onfulfilled, onrejected){
//
// NOTE: this is internally linked to the actual (via: ..then.call(this, ..))
// state and will be resolved in .__new__(..) below.
then: function(onfulfilled, onrejected){
var p = new Promise(
function(resolve, reject){
Promise.prototype.then.call(this,
@ -525,6 +572,47 @@ object.Constructor('IterablePromise', Promise, {
return arguments.length > 0 ?
p.then(...arguments)
: p },
// XXX EXPEREMENTAL
// Like .then(..) but returns an IterablePromise instance...
iterthen: function(onfulfilled, onrejected){
if(this.isSync()){
var res = onfulfilled ?
this.constructor(onfulfilled(this.__unpack()))
: this.constructor(this.__unpack())
onrejected
&& res.catch(onrejected)
return res }
// XXX we need to feed the output of onfulfilled to the value of
// res, but to this without wrapping the whole thing in a
// promise (possible???)...
return arguments.length > 0 ?
this.constructor(this.then(...arguments))
: this.constructor(this.__packed, 'raw') },
// XXX EXPEREMENTAL
isSync: function(){
return !(this.__packed instanceof Promise
|| this.__packed
.filter(function(e){
return e instanceof IterablePromise ?
!e.isSync()
: e instanceof Promise
&& !(e instanceof SyncPromise) })
.length > 0) },
sync: function(error=false){
try{
var res = this.__unpack()
}catch(err){
if(error == false){
return }
if(typeof(error) == 'function'){
return error(err) }
throw err }
return error !== false
&& res instanceof Promise ?
// XXX should this be an IterablePromise???
res.catch(error)
: res },
// constructor...
@ -584,18 +672,37 @@ object.Constructor('IterablePromise', Promise, {
// handle/pack input data...
if(handler != 'raw'){
list = list instanceof IterablePromise ?
this.__handle(list.__packed, handler)
: this.__pack(list, handler) }
obj.__handle(list.__packed, handler)
: obj.__pack(list, handler) }
Object.defineProperty(obj, '__packed', {
value: list,
enumerable: false,
// NOTE: this is needed for self-resolve...
writable: true,
})
// handle promise state...
this.__unpack(list)
.then(function(list){
promise.resolve(list) })
.catch(promise.reject) }
try{
var res = obj.__unpack(list)
}catch(err){
promise.reject(err) }
res instanceof Promise ?
res
.then(function(list){
promise.resolve(list) })
.catch(promise.reject)
: promise.resolve(res)
// XXX EXPEREMENTAL
// XXX do we handle errors here???
// self-resolve state...
list instanceof Promise ?
list.then(function(list){
obj.__packed = list })
: list.forEach(function(elem, i){
elem instanceof Promise
&& elem.then(function(elem){
lst = obj.__packed.slice()
lst[i] = elem
obj.__packed = lst }) }) }
return obj },
})
@ -863,6 +970,13 @@ object.Constructor('SyncPromise', Promise, {
resolve(this.value))
// XXX should we return a copy???
: this },
sync: function(error='throw'){
if(error !== false
&& 'error' in this){
if(typeof(error) != 'function'){
throw this.error }
return error(this.error) }
return this.value },
// NOTE: if func calls resolve(..) with a promise then this will return
// that promise...
@ -894,6 +1008,7 @@ object.Constructor('SyncPromise', Promise, {
})
//---------------------------------------------------------------------
var PromiseMixin =
@ -948,16 +1063,7 @@ object.Mixin('PromiseProtoMixin', 'soft', {
as: ProxyPromise,
iter: function(handler=undefined){
return IterablePromise(this, handler) },
// XXX should we try and return a sync value if normal promise is resolved???
// ...sould need to hook .then(..) to do this...
sync: function(error='throw'){
if(this instanceof SyncPromise){
if(error !== false
&& 'error' in this){
if(typeof(error) != 'function'){
throw this.error }
return error(this.error) }
return this.value }
sync: function(){
return this },
})

View File

@ -77,7 +77,7 @@ Library of JavaScript type extensions, types and utilities.
- [`<promise>.iter()`](#promiseiter)
- [`<promise-iter>.iter()`](#promise-iteriter)
- [`<promise-iter>.map(..)` / `<promise-iter>.filter(..)` / `<promise-iter>.reduce(..)`](#promise-itermap--promise-iterfilter--promise-iterreduce)
- [`<promise-iter>.between(..)`](#promise-iterbetween)
- [`<promise-iter>.between(..)`](#promise-iterbetween)
- [`<promise-iter>.flat(..)`](#promise-iterflat)
- [`<promise-iter>.reverse()`](#promise-iterreverse)
- [`<promise-iter>.concat(..)`](#promise-iterconcat)
@ -95,7 +95,7 @@ Library of JavaScript type extensions, types and utilities.
- [`<promise>.as(..)`](#promiseas)
- [`<promise-proxy>.<method>(..)`](#promise-proxymethod)
- [Sync/async promise](#syncasync-promise)
- [`Promise.sync(..)` / `promise.SyncPromice(..)`](#promisesync--promisesyncpromice)
- [`Promise.sync(..)` / `promise.SyncPromise(..)`](#promisesync--promisesyncpromise)
- [`<promise>.sync(..)`](#promisesync)
- [`<sync-promise>.value` / `<sync-promise>.error`](#sync-promisevalue--sync-promiseerror)
- [`Promise.sync.all(..)` / `Promise.sync.allSettled(..)` / `Promise.sync.any(..)` / `Promise.sync.race(..)`](promisesyncall--promisesyncallsettled--promisesyncany--promisesyncrace`)
@ -1706,7 +1706,7 @@ Note that since `.reduce(..)` handler's execution order can not be known,
there is no point in implementing `.reduceRigth(..)`.
### `<promise-iter>.between(..)`
#### `<promise-iter>.between(..)`
```bnf
<promise-iter>.between(<value>)
@ -2009,7 +2009,7 @@ the main `<promise>` is resolved.
### Sync/async promise
#### `Promise.sync(..)` / `promise.SyncPromice(..)`
#### `Promise.sync(..)` / `promise.SyncPromise(..)`
```dnf
Promise.sync(<func>)