diff --git a/Promise.js b/Promise.js index e3e9275..65ed0b1 100644 --- a/Promise.js +++ b/Promise.js @@ -124,137 +124,6 @@ object.Constructor('IterablePromise', Promise, { // handlers in sequence. // XXX EXPEREMENTAL: STOP... // XXX ITER can we unwind (sync/async) generators one by one??? - /* XXX this repeats part of the functionality of .__handle(..) - __pack: function(list, handler=undefined, onerror=undefined){ - var that = this - // handle iterator... - // XXX ITER do we unwind the iterator here or wait to unwind later??? - if(typeof(list) == 'object' - && Symbol.iterator in list){ - if(!onerror){ - list = [...list] - // handle errors in input generator... - // NOTE: this does not offer the most control because semantically - // we bust behave in the same manner as .iter(..) - } else { - var res = [] - try{ - for(var e of list){ - res.push(e) } - }catch(err){ - var r = onerror(err) - r !== undefined - && res.push(r) } - list = res } } - // handle iterable promise... - if(list instanceof IterablePromise){ - return this.__handle(list.__packed, handler, onerror) } - // handle promise / async-iterator... - // XXX ITER do we unwind the iterator here or wait to unwind later??? - if(typeof(list) == 'object' - && Symbol.asyncIterator in list){ - return list - .iter(handler, onerror) - .then(function(list){ - return that.__pack(list) }) } - if(list instanceof Promise){ - return list - .then(function(list){ - return that.__pack(list, handler, onerror) }) } - // do the work... - // NOTE: packing and handling are mixed here because it's faster - // to do them both on a single list traverse... - var handle = !!handler - handler = handler - ?? function(elem){ - return [elem] } - // XXX this repeats .__handle(..), need to unify... - var stop = false - var map = 'map' - 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 ? - (handleSTOP ? - // stoppable -- need to handle stop async... - elem - .then(function(res){ - return !stop ? - handler(res) - : [] }) - // NOTE: we are using .catch(..) here - // instead of directly passing the - // error handler to be able to catch - // the STOP from the handler... - .catch(handleSTOP) - // non-stoppable... - : elem.then(handler)) - : elem instanceof Array ? - handler(elem) - // NOTE: we keep things that do not need protecting - // from .flat() as-is... - : !handle ? - elem - : handler(elem) }) } - // pack (stoppable)... - if(!!this.constructor.STOP){ - map = 'smap' - var handleSTOP = function(err){ - // handle stop... - stop = err - if(err === that.constructor.STOP - || err instanceof that.constructor.STOP){ - return 'value' in err ? - err.value - : [] } - throw err } - try{ - return pack() - }catch(err){ - return handleSTOP(err) } } - - // pack (non-stoppable)... - return pack() }, - // transform/handle packed array (sync, but can return promises in the list)... - __handle: function(list, handler=undefined, onerror=undefined){ - var that = this - if(typeof(list) == 'function'){ - handler = list - list = this.__packed } - if(!handler){ - return list } - // handle promise list... - if(list instanceof Promise){ - return list.then(function(list){ - return that.__handle(list, handler, onerror) }) } - // do the work... - // NOTE: since each section of the packed .__array is the same - // structure as the input we'll use .__pack(..) to handle - // them, this also keeps all the handling code in one place. - var map = !!this.constructor.STOP ? - 'smap' - : 'map' - return list[map](function(elem){ - elem = elem instanceof Array - || elem instanceof Promise ? - that.__pack(elem, handler, onerror) - : [handler(elem)] - elem = elem instanceof Promise ? - elem.then(function([e]){ - return e }) - : elem - return elem }) - .flat() }, - /*/ __pack: function(list, handler=undefined, onerror=undefined){ var that = this // handle iterator... @@ -296,9 +165,12 @@ object.Constructor('IterablePromise', Promise, { .map(function(elem){ return elem instanceof Array ? [elem] + /* XXX PROMISE_WRAP + /*/ : elem instanceof Promise ? elem.then(function(e){ return [e] }) + //*/ : elem }) // handle if needed... return handler ? @@ -354,7 +226,13 @@ object.Constructor('IterablePromise', Promise, { // NOTE: the promise protects this from .flat() elem.then(function(elem){ return !stop ? + // XXX this should be the same as the non-promise version... + // (see: .filter(..)) + /* XXX PROMISE_WRAP + [each(elem)] + /*/ each(elem) + //*/ : [] }) : elem instanceof Array ? [each(elem)] @@ -363,7 +241,6 @@ object.Constructor('IterablePromise', Promise, { function(){ stop = true }) .flat() }, - //*/ // XXX this should return IterablePromise if .__packed is partially sync (???) // unpack array (sync/async)... __unpack: function(list){ @@ -386,8 +263,11 @@ object.Constructor('IterablePromise', Promise, { // XXX can we return an IterablePromise??? // XXX is there a more elegant way to do this??? return Promise.all(list) + /* XXX PROMISE_WRAP + /*/ .then(function(list){ return list.flat() }) + //*/ .iter() } res.push(e) } return res.flat() }, @@ -415,22 +295,40 @@ object.Constructor('IterablePromise', Promise, { map: function(func){ return this.constructor(this, function(e){ + /* XXX PROMISE_WRAP var res = func(e) return res instanceof Promise ? - res.then(function(e){ + res.then(function(e){ return [e] }) : [res] }) }, + /*/ + return [func(e)] }) }, + //*/ + // XXX BUG: + // await Promise.iter([1, [2], 3]) + // .filter(e => Promise.resolve(false)) + // -> [ [] ] + // should be: + // -> [] + // ...do we flatten the result of promise returned by handler??? + // XXX this might require a change in .__handle(..) filter: function(func){ return this.constructor(this, function(e){ var res = func(e) - var _filter = function(elem){ - return res ? - [elem] - : [] } return res instanceof Promise ? - res.then(_filter) - : _filter(e) }) }, + res.then(function(res){ + // XXX this should be the same as the non-promise version... + return res ? + /* XXX PROMISE_WRAP + [e] + /*/ + e + //*/ + : [] }) + : res ? + [e] + : [] }) }, // NOTE: this does not return an iterable promise as we can't know // what the user reduces to... // NOTE: the items can be handled out of order because the nested @@ -861,6 +759,9 @@ object.Constructor('IterableSequentialPromise', IterablePromise, { && i < list.length-1){ res.push(e .then(function(e){ + // NOTE: this does not call any handlers, thus + // there should be no risk of out of order + // handler execution.... return seqiter( [e, ...list.slice(i+1)]) .flat() })) @@ -873,6 +774,7 @@ object.Constructor('IterableSequentialPromise', IterablePromise, { list = list instanceof SyncPromise ? list.sync() : list + // repack... list = list instanceof Array ? repack(list) : list.then ? diff --git a/package-lock.json b/package-lock.json index 054ce44..4cd6101 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ig-types", - "version": "6.24.11", + "version": "6.24.16", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ig-types", - "version": "6.24.11", + "version": "6.24.16", "license": "BSD-3-Clause", "dependencies": { "ig-object": "^6.0.0", @@ -359,9 +359,9 @@ "integrity": "sha512-KxS8AGsjelRAmbbQuASj+XRuk99P4OOprd+lIUMU2nuRKPQItNQK/apls8IlR3kNp5ZdQqBdV+zVJmYGrxofnA==" }, "node_modules/ig-test": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/ig-test/-/ig-test-1.5.4.tgz", - "integrity": "sha512-1N2U3v6tSPGUeBNnCuFWem09ngNkuMT1NGEEyVtA73VR5nz8GMbroifiPX0+j/GLqBJ/u39bsMCdZSMaIMTRdg==", + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/ig-test/-/ig-test-1.5.9.tgz", + "integrity": "sha512-6X/bO7yYh7B+DEJEFEVCQAwECs/EPr64h3V8xJbn69lWNBeSo08dkXEYdPkgU8sXDjZOh3cfvz7hcREnZHjpSQ==", "dev": true, "dependencies": { "colors": "1.4.0", @@ -1078,9 +1078,9 @@ "integrity": "sha512-KxS8AGsjelRAmbbQuASj+XRuk99P4OOprd+lIUMU2nuRKPQItNQK/apls8IlR3kNp5ZdQqBdV+zVJmYGrxofnA==" }, "ig-test": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/ig-test/-/ig-test-1.5.4.tgz", - "integrity": "sha512-1N2U3v6tSPGUeBNnCuFWem09ngNkuMT1NGEEyVtA73VR5nz8GMbroifiPX0+j/GLqBJ/u39bsMCdZSMaIMTRdg==", + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/ig-test/-/ig-test-1.5.9.tgz", + "integrity": "sha512-6X/bO7yYh7B+DEJEFEVCQAwECs/EPr64h3V8xJbn69lWNBeSo08dkXEYdPkgU8sXDjZOh3cfvz7hcREnZHjpSQ==", "dev": true, "requires": { "colors": "1.4.0", diff --git a/test.js b/test.js index 5a3aeae..99c29eb 100755 --- a/test.js +++ b/test.js @@ -112,9 +112,9 @@ var cases = test.Cases({ }, IterablePromise: test.TestSet(function(){ - var create = function(assert, value){ + var create = function(assert, value, expected){ return { - input: value, + input: expected ?? value, output: assert(Promise.iter(value), 'Promise.iter(', value, ')'), } } @@ -136,21 +136,17 @@ var cases = test.Cases({ array_mixed: function(assert){ return create(assert, [1, Promise.resolve(2), 3]) }, nested_array_mixed: function(assert){ - return create(assert, [ - 1, - Promise.resolve(2), - [3], - Promise.resolve([4]), - ]) }, + return create(assert, + [1, Promise.resolve(2), [3], Promise.resolve([4])], + [1, 2, [3], [4]]) }, promise_array_mixed: function(assert){ - return create(assert, Promise.resolve([1, Promise.resolve(2), 3])) }, + return create(assert, + Promise.resolve([1, Promise.resolve(2), 3]), + [1, 2, 3]) }, promise_nested_array_mixed: function(assert){ - return create(assert, Promise.resolve([ - 1, - Promise.resolve(2), - [3], - Promise.resolve([4]), - ])) }, + return create(assert, + Promise.resolve([1, Promise.resolve(2), [3], Promise.resolve([4])]), + [1, 2, [3], [4]]) }, }) this.Modifier({ nest: function(assert, setup){ @@ -182,6 +178,19 @@ var cases = test.Cases({ output: setup.output .filter(function(e){ return false }), } }, + filter_promise_all: function(assert, setup){ + setup.output = setup.output + .filter(function(e){ + return Promise.resolve(true) }) + return setup }, + filter_promise_none: function(assert, setup){ + return { + input: [], + output: setup.output + .filter(function(e){ + return Promise.resolve(false) }), + } }, + //*/ /* XXX need tuning... concat_basic: function(assert, {input, output}){ @@ -300,6 +309,23 @@ var cases = test.Cases({ [1,2,3], 'flat unpack', meth) } + + var order = [] + await Promise.seqiter([ + 1, + Promise.resolve(2), + Promise.all([3,4]), + Promise.seqiter([5]), + 6, + ]) + .flat() + .map(function(e){ + order.push(e) + return e }) + assert.array( + order, + [1,2,3,4,5,6], + 'Promise.seqiter(..) handle order') }, // Date.js @@ -557,8 +583,8 @@ Events.cases({ // test event list... - assert.array(obj.events, ['event', 'eventBlank'], '.events') - assert.array(obj.eventful, ['bareEvent', 'bareEventBlank'], '.eventful') + assert.array(obj.events, ['eventBlank', 'event'], '.events') + assert.array(obj.eventful, ['bareEventBlank', 'bareEvent'], '.eventful') // bind... var bind = function(evt){