diff --git a/Array.js b/Array.js index 364258c..55fda52 100644 --- a/Array.js +++ b/Array.js @@ -524,19 +524,27 @@ object.Mixin('ArrayProtoMixin', 'soft', { // XXX this should handle throwing STOP!!! // ...might also ne a good idea to isolate the STOP mechanics // into a spearate module/package... - iter: stoppable(function*(handler=undefined){ - if(handler){ - var i = 0 - for(var e of this){ - var res = handler.call(this, e, i++) - // treat non-iterables as single elements... - if(typeof(res) == 'object' - && Symbol.iterator in res){ - yield* res - } else { - yield res } } - } else { - yield* this }}), + iter: stoppable( + function*(handler=undefined, onstop){ + if(handler){ + var i = 0 + for(var e of this){ + var res = handler.call(this, e, i++) + // treat non-iterables as single elements... + if(typeof(res) == 'object' + && Symbol.iterator in res){ + yield* res + } else { + yield res } } + } else { + yield* this }}, + // handle stops is onstop(..) is defined... + function(res, _, onstop){ + typeof(onstop) == 'function' + && onstop.call(this, + ...(res === STOP ? + [] + : [res])) }), // Stoppable iteration... diff --git a/Promise.js b/Promise.js index ea37c09..efbcf32 100644 --- a/Promise.js +++ b/Promise.js @@ -255,16 +255,6 @@ object.Constructor('IterablePromise', Promise, { return elem }) .flat() }, /*/ - // XXX BUG: - // await Promise.iter([Promise.all([1,2,3])], e => e) - // await Promise.iter([Promise.iter([1,2,3])], e => e) - // -> [1] - // the issue is in .__handle(..)'s - // elem = elem instanceof Promise ? - // elem.then(function([e]){ - // // XXX - // return e }) - // : ... __pack: function(list, handler=undefined, onerror=undefined){ var that = this // handle iterator... @@ -315,6 +305,7 @@ object.Constructor('IterablePromise', Promise, { this.__handle(list, handler, onerror) : list }, // transform/handle packed array (sync, but can return promises in the list)... + // XXX need a strict spec... __handle: function(list, handler=undefined, onerror=undefined){ var that = this if(typeof(list) == 'function'){ @@ -335,17 +326,20 @@ object.Constructor('IterablePromise', Promise, { : 'map' return list [map](function(elem){ - // XXX need a strict spec... return elem instanceof IterablePromise ? + // XXX should this be expanded??? (like Array below) (elem.isSync() ? handler(elem.sync()) // XXX need to handle this but keep it IterablePromise... : elem.iterthen(handler)) : (elem instanceof SyncPromise && !(elem.sync() instanceof Promise)) ? + // XXX should this be expanded??? (like Array below) handler(elem.sync()) + // promise / promise-like... : elem && elem.then ? - // XXX handle STOP... + // XXX handle STOP -- no need to call handlers after a STOP... + // ...is there a way to detect STOP from inside .smap(..) ??? elem.then(function(elem){ return handler( elem.length == 1 ? diff --git a/generator.js b/generator.js index 5b3fce1..dd9ca41 100644 --- a/generator.js +++ b/generator.js @@ -91,23 +91,36 @@ function*(lst=[]){ } else { yield lst } } +// handle stops is onstop(..) is defined... +var __onstop = +function(res, _, ...args){ + var onstop = args.at(-1) + typeof(onstop) == 'function' + && onstop.call(this, + ...(res === STOP ? + [] + : [res])) } + + // XXX updatae Array.js' version for compatibility... // XXX DOCS!!! var iter = module.iter = Generator.iter = - stoppable(function(lst=[]){ - // handler -> generator-constructor... - if(typeof(lst) == 'function'){ - // we need to be callable... - var that = this instanceof Function ? - this - // generic root generator... - : module.__iter - return function*(){ - yield* that(...arguments).iter(lst) } } - // no handler -> generator instance... - return module.__iter(lst) }) + stoppable( + function(lst=[]){ + // handler -> generator-constructor... + if(typeof(lst) == 'function'){ + // we need to be callable... + var that = this instanceof Function ? + this + // generic root generator... + : module.__iter + return function*(){ + yield* that(...arguments).iter(lst) } } + // no handler -> generator instance... + return module.__iter(lst) }, + __onstop) // NOTE: we need .iter(..) to both return generators if passed an iterable // and genereator constructos if passed a function... @@ -308,31 +321,33 @@ var GeneratorProtoMixin = module.GeneratorProtoMixin = object.Mixin('GeneratorProtoMixin', 'soft', { // XXX use module.iter(..) ??? - iter: stoppable(function*(handler, onerror){ - try{ - if(handler){ - var i = 0 - for(var elem of this){ - var res = handler.call(this, elem, i) - // expand iterables... - if(typeof(res) == 'object' - && Symbol.iterator in res){ - yield* res - // as-is... - } else { - yield res }} - // no handler... - } else { - yield* this } - }catch(err){ - if(onerror){ - if(!(err === STOP - || err instanceof STOP)){ - var res = onerror(err) - if(res){ - yield res - return } } } - throw err }}), + iter: stoppable( + function*(handler, onerror){ + try{ + if(handler){ + var i = 0 + for(var elem of this){ + var res = handler.call(this, elem, i) + // expand iterables... + if(typeof(res) == 'object' + && Symbol.iterator in res){ + yield* res + // as-is... + } else { + yield res }} + // no handler... + } else { + yield* this } + }catch(err){ + if(onerror){ + if(!(err === STOP + || err instanceof STOP)){ + var res = onerror(err) + if(res){ + yield res + return } } } + throw err }}, + __onstop), //*/ at: function(i){ @@ -396,8 +411,10 @@ object.Mixin('GeneratorProtoMixin', 'soft', { yield* func(e, i++, this) } } else { for(var e of this){ - yield func(e, i++, this) } } }), - filter: stoppable(function*(func){ + yield func(e, i++, this) } } }, + __onstop), + filter: stoppable( + function*(func){ var i = 0 try{ for(var e of this){ @@ -409,28 +426,36 @@ object.Mixin('GeneratorProtoMixin', 'soft', { if(!err.value){ throw STOP } err.value = e } - throw err } }), + throw err } }, + __onstop), - reduce: stoppable(function(func, res){ - var i = 0 - for(var e of this){ - res = func(res, e, i++, this) } - return res }), + reduce: stoppable( + function(func, res){ + var i = 0 + for(var e of this){ + res = func(res, e, i++, this) } + return res }, + // NOTE: we need to wrap __onstop(..) here to prevent res if it + // was passed a function from ever being treated as onstop(..)... + function(res, f, _, onstop){ + return __onstop.call(this, res, onstop) }), greduce: function*(func, res){ yield this.reduce(...arguments) }, - between: stoppable(function*(func){ - var i = 0 - var j = 0 - var prev - for(var e of this){ - if(i > 0){ - yield typeof(func) == 'function' ? - func.call(this, [prev, e], i-1, i + j++, this) - : func } - prev = e - yield e - i++ } }), + between: stoppable( + function*(func){ + var i = 0 + var j = 0 + var prev + for(var e of this){ + if(i > 0){ + yield typeof(func) == 'function' ? + func.call(this, [prev, e], i-1, i + j++, this) + : func } + prev = e + yield e + i++ } }, + __onstop), // NOTE: this is a special case in that it will unwind the generator... // NOTE: this is different from .forEach(..) in that this will @@ -566,29 +591,31 @@ object.Mixin('AsyncGeneratorProtoMixin', 'soft', { return this.unwind.finally(...arguments) }, // XXX might be a good idea to use this approach above... - iter: stoppable(async function*(handler=undefined, onerror=undefined){ - try{ - var i = 0 - if(handler){ - for await(var e of this){ - var res = handler.call(this, e, i++) - if(typeof(res) == 'object' - && Symbol.iterator in res){ - yield* res - } else { - yield res } } - } else { - yield* this } - }catch(err){ - if(onerror){ - if(!(err === STOP || err instanceof STOP)){ - var res = onerror(err) - if(res !== undefined){ - yield handler ? - handler(res) - : res } - return } } - throw err } }), + iter: stoppable( + async function*(handler=undefined, onerror=undefined){ + try{ + var i = 0 + if(handler){ + for await(var e of this){ + var res = handler.call(this, e, i++) + if(typeof(res) == 'object' + && Symbol.iterator in res){ + yield* res + } else { + yield res } } + } else { + yield* this } + }catch(err){ + if(onerror){ + if(!(err === STOP || err instanceof STOP)){ + var res = onerror(err) + if(res !== undefined){ + yield handler ? + handler(res) + : res } + return } } + throw err } }, + __onstop), map: async function*(func){ yield* this.iter(function(elem, i){ @@ -686,9 +713,11 @@ function*(value=true, stop){ var produce = module.produce = -stoppable(function*(func){ - while(true){ - yield func() } }) +stoppable( + function*(func){ + while(true){ + yield func() } }, + __onstop) diff --git a/package-lock.json b/package-lock.json index 74b6326..054ce44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ig-types", - "version": "6.24.10", + "version": "6.24.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ig-types", - "version": "6.24.10", + "version": "6.24.11", "license": "BSD-3-Clause", "dependencies": { "ig-object": "^6.0.0", @@ -354,9 +354,9 @@ } }, "node_modules/ig-stoppable": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ig-stoppable/-/ig-stoppable-2.0.1.tgz", - "integrity": "sha512-vos7eFNHryIg6yNzrWTV1QP1Sja5JkMU3nL2Y/YvSgU74l7w1Rx6yfVKoEHwNOex4MCkLsq6wMa91Ac6IWOtIA==" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/ig-stoppable/-/ig-stoppable-2.0.4.tgz", + "integrity": "sha512-KxS8AGsjelRAmbbQuASj+XRuk99P4OOprd+lIUMU2nuRKPQItNQK/apls8IlR3kNp5ZdQqBdV+zVJmYGrxofnA==" }, "node_modules/ig-test": { "version": "1.5.4", @@ -1073,9 +1073,9 @@ } }, "ig-stoppable": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ig-stoppable/-/ig-stoppable-2.0.1.tgz", - "integrity": "sha512-vos7eFNHryIg6yNzrWTV1QP1Sja5JkMU3nL2Y/YvSgU74l7w1Rx6yfVKoEHwNOex4MCkLsq6wMa91Ac6IWOtIA==" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/ig-stoppable/-/ig-stoppable-2.0.4.tgz", + "integrity": "sha512-KxS8AGsjelRAmbbQuASj+XRuk99P4OOprd+lIUMU2nuRKPQItNQK/apls8IlR3kNp5ZdQqBdV+zVJmYGrxofnA==" }, "ig-test": { "version": "1.5.4",