ig-types 6.24.16 → 6.24.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/Promise.js +334 -275
  2. package/package.json +1 -1
  3. package/runner.js +32 -12
  4. package/test.js +167 -18
package/Promise.js CHANGED
@@ -34,6 +34,292 @@ var generator = require('./generator')
34
34
 
35
35
 
36
36
 
37
+ //---------------------------------------------------------------------
38
+
39
+ // packed format API...
40
+ //
41
+ // This is separate from IterablePromise to enable low-level testing and
42
+ // experimentation.
43
+ //
44
+ // Packed format
45
+ // [
46
+ // <value>,
47
+ //
48
+ // [
49
+ // <value>,
50
+ //
51
+ // <promise( <value> )>,
52
+ //
53
+ // ..
54
+ // ],
55
+ //
56
+ // <promise( <value> )>,
57
+ //
58
+ // <promise( [ <value>, .. ] )>,
59
+ //
60
+ // ..
61
+ // ]
62
+ //
63
+ // <packed> ::=
64
+ // <value>
65
+ // | <packed-array>
66
+ // | <promise( <value> )>
67
+ // | <promise( <packed-array> )>
68
+ //
69
+ // <packed-array> ::=
70
+ // [ <packed-item>, .. ]
71
+ //
72
+ // <packed-item> ::=
73
+ // <value>
74
+ // | [ <nested-value>, .. ]
75
+ // | <promise( <value> )>
76
+ // | <promise( [ <value>, .. ] )>
77
+ //
78
+ // <nested-value> ::=
79
+ // <value>
80
+ // | <promise( <value> )>
81
+ //
82
+ //
83
+ //
84
+ // XXX revise bnf...
85
+ // XXX should this be a container or just a set of function???
86
+ // ...for simplicity I'll keep it a stateless set of functions for
87
+ // now, to avoid yet another layer of indirection -- IterablePromise...
88
+ var packed =
89
+ module.packed =
90
+ {
91
+ //
92
+ // pack(<array>)
93
+ // pack(<promise>)
94
+ // -> <packed>
95
+ //
96
+ // NOTE: when all promises are expanded the packed array can be unpacked
97
+ // by simply calling .flat()
98
+ // NOTE: if 'types/Array' is imported this will support throwing STOP
99
+ // from the handler.
100
+ // Due to the async nature of promises though the way stops are
101
+ // handled may be unpredictable -- the handlers can be run out
102
+ // of order, as the nested promises resolve and thus throwing
103
+ // stop will stop the handlers not yet run and not the next
104
+ // handlers in sequence.
105
+ // XXX does this need onerror(..) ???
106
+ // ...essentially this can make sense only in generator expansion,
107
+ // but not sure if this should be done here as it is sync...
108
+ pack: function(list){
109
+ var that = this
110
+ // list: promise...
111
+ // XXX should we just test for typeof(list.then) == 'function'???
112
+ // XXX should we expand async generators here???
113
+ if(list instanceof Promise
114
+ // list: async promise...
115
+ || (typeof(list) == 'object'
116
+ && list.then
117
+ && Symbol.asyncIterator in list)){
118
+ return list
119
+ .then(this.pack.bind(this)) }
120
+ // list: generator...
121
+ if(typeof(list) == 'object'
122
+ && !list.map
123
+ && Symbol.iterator in list){
124
+ list = [...list] }
125
+ /* XXX on one hand this should be here and on the other I'm not
126
+ // sure how are we going to thread handler and onerror to
127
+ // here...
128
+ // list: promise / async-iterator...
129
+ if(typeof(list) == 'object'
130
+ && Symbol.asyncIterator in list){
131
+ return list
132
+ // XXX
133
+ .iter(handler, onerror)
134
+ .then(function(list){
135
+ return that.pack(list) }) }
136
+ //*/
137
+ // list: non-array / non-iterable...
138
+ if(typeof(list.map) != 'function'){
139
+ list = [list].flat() }
140
+ // list: array...
141
+ return list
142
+ .map(function(elem){
143
+ // XXX should we expand generators here???
144
+ return elem instanceof Array ?
145
+ [elem]
146
+ // XXX should we just test for .then(..)???
147
+ : elem instanceof Promise ?
148
+ elem.then(function(elem){
149
+ return elem instanceof Array ?
150
+ [elem]
151
+ : elem })
152
+ : elem }) },
153
+ //
154
+ // handle(<packed>, <handler>[, <onerror>])
155
+ // -> <packed>
156
+ //
157
+ handle: function(packed, handler, onerror){
158
+ var that = this
159
+ var handlers = [...arguments].slice(1)
160
+
161
+ if(packed instanceof Promise){
162
+ return packed.then(function(packed){
163
+ return that.handle(packed, ...handlers) }) }
164
+
165
+ var handleSTOP = function(err){
166
+ if(err && err === Array.STOP){
167
+ stop = true
168
+ return []
169
+ } else if(err && err instanceof Array.STOP){
170
+ return err.value }
171
+ throw err }
172
+
173
+ var stop = false
174
+ var map = Array.STOP ?
175
+ 'smap'
176
+ : 'map'
177
+ return packed
178
+ // NOTE: we do not need to rapack after this because the handlers
179
+ // will get the correct (unpacked) values and it's their
180
+ // responsibility to pack them if needed...
181
+ // NOTE: this removes the need to handle sub-arrays unless they are
182
+ // in a promise...
183
+ .flat()
184
+ [map](
185
+ function(elem){
186
+ return elem instanceof Promise ?
187
+ elem.then(function(elem){
188
+ if(stop){
189
+ return [] }
190
+ try{
191
+ var has_promise = false
192
+ // NOTE: do the same thing handle(..) does
193
+ // but on a single level, without expanding
194
+ // arrays...
195
+ if(elem instanceof Array){
196
+ var res = elem.map(function(elem){
197
+ var res = elem instanceof Promise ?
198
+ elem.then(function(elem){
199
+ try{
200
+ return !stop ?
201
+ handler(elem)
202
+ : []
203
+ } catch(err){
204
+ return handleSTOP(err) } })
205
+ : handler(elem)
206
+ has_promise = has_promise
207
+ || res instanceof Promise
208
+ return res })
209
+ // non-arrays...
210
+ } else {
211
+ // NOTE: we are wrapping the result in an array to
212
+ // normalize it with the above...
213
+ res = [handler(elem)]
214
+ has_promise = has_promise
215
+ || res[0] instanceof Promise }
216
+
217
+ // compensate for the outer .flat()...
218
+ // NOTE: at this point res is always an array...
219
+ return has_promise ?
220
+ // NOTE: since we are already in a promise
221
+ // grouping things here is not a big
222
+ // deal, however this is needed to link
223
+ // nested promises with the containing
224
+ // promise...
225
+ Promise.all(res)
226
+ .then(function(res){
227
+ return res.flat() })
228
+ : res.flat()
229
+ } catch(err){
230
+ return handleSTOP(err) } })
231
+ : handler(elem) },
232
+ // onerror...
233
+ function(err){
234
+ stop = true
235
+ typeof(onerror) == 'function'
236
+ && onerror(err) }) },
237
+ //
238
+ // unpack(<packed>)
239
+ // -> <array>
240
+ //
241
+ unpack: function(packed){
242
+ if(packed instanceof Promise){
243
+ return packed.then(this.unpack.bind(this)) }
244
+ var input = packed
245
+ // NOTE: we expand promises on the first two levels of the packed
246
+ // list and then flatten the result...
247
+ for(var [i, elem] of Object.entries(packed)){
248
+ // expand promises in lists...
249
+ if(elem instanceof Array){
250
+ for(var e of elem){
251
+ if(e instanceof Promise){
252
+ // copy the input (on demand) list as we'll need to
253
+ // modify it...
254
+ packed === input
255
+ && (packed = packed.slice())
256
+ // NOTE: this will immediately be caught by the
257
+ // Promise condition below...
258
+ elem = packed[i] =
259
+ Promise.all(elem)
260
+ break } } }
261
+ // expand promises...
262
+ if(elem instanceof Promise){
263
+ return Promise.all(packed)
264
+ .then(this.unpack.bind(this)) } }
265
+ // the list is expanded here...
266
+ return packed.flat() },
267
+ }
268
+
269
+
270
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
271
+
272
+ //
273
+ // itemResolved(<list>, <onupdate>[, <ondone>])
274
+ // itemResolved(<list>, <options>)
275
+ // -> <list>
276
+ //
277
+ // <onupdate>(<elem>, <index>, <list>)
278
+ //
279
+ // <ondone>(<list>)
280
+ //
281
+ // options format:
282
+ // {
283
+ // onupdate: <func>,
284
+ //
285
+ // // optional
286
+ // ondone: <func>,
287
+ // }
288
+ //
289
+ // XXX rename to Promise.each(..) or Promise.eachPromise(..) ???
290
+ var itemResolved =
291
+ module.itemResolved =
292
+ function(list, onupdate, ondone){
293
+ if(typeof(onupdate) != 'function'){
294
+ var {onupdate, ondone} = onupdate }
295
+ typeof(ondone) == 'function'
296
+ && Promise.all(list)
297
+ .then(ondone)
298
+ for(let [i, elem] of Object.entries(list)){
299
+ if(elem instanceof Promise){
300
+ elem.then(function(elem){
301
+ onupdate(elem, i, list) }) } }
302
+ return list }
303
+
304
+ // Like itemResolved(..) but both onupdate and ondone are optional...
305
+ //
306
+ // XXX do not modify the array after this is called and until ondone(..) is called...
307
+ // XXX should this know how to expand things???
308
+ var selfResolve =
309
+ module.selfResolve =
310
+ function(list, onupdate, ondone){
311
+ if(onupdate
312
+ && typeof(onupdate) != 'function'){
313
+ var {onupdate, ondone} = onupdate }
314
+ return itemResolved(list,
315
+ function(elem, i, list){
316
+ list[i] = elem
317
+ typeof(onupdate) == 'function'
318
+ && onupdate(elem, i, list) },
319
+ ondone) }
320
+
321
+
322
+
37
323
  //---------------------------------------------------------------------
38
324
  // Iterable promise...
39
325
  //
@@ -63,6 +349,9 @@ var generator = require('./generator')
63
349
  // XXX add support for async generators...
64
350
  //
65
351
 
352
+ var EMPTY = {doc: 'empty placeholder'}
353
+
354
+
66
355
  var iterPromiseProxy =
67
356
  module.iterPromiseProxy =
68
357
  function(name){
@@ -114,284 +403,45 @@ object.Constructor('IterablePromise', Promise, {
114
403
  // NOTE: these can be useful for debugging and extending...
115
404
  //
116
405
  // pack and oprionally transform/handle an array (sync)...
117
- //
118
- // NOTE: if 'types/Array' is imported this will support throwing STOP
119
- // from the handler.
120
- // Due to the async nature of promises though the way stops are
121
- // handled may be unpredictable -- the handlers can be run out
122
- // of order, as the nested promises resolve and thus throwing
123
- // stop will stop the handlers not yet run and not the next
124
- // handlers in sequence.
125
- // XXX EXPEREMENTAL: STOP...
126
- // XXX ITER can we unwind (sync/async) generators one by one???
127
- /* XXX this repeats part of the functionality of .__handle(..)
128
406
  __pack: function(list, handler=undefined, onerror=undefined){
129
- var that = this
130
- // handle iterator...
131
- // XXX ITER do we unwind the iterator here or wait to unwind later???
132
- if(typeof(list) == 'object'
133
- && Symbol.iterator in list){
134
- if(!onerror){
135
- list = [...list]
136
- // handle errors in input generator...
137
- // NOTE: this does not offer the most control because semantically
138
- // we bust behave in the same manner as <generator>.iter(..)
139
- } else {
140
- var res = []
141
- try{
142
- for(var e of list){
143
- res.push(e) }
144
- }catch(err){
145
- var r = onerror(err)
146
- r !== undefined
147
- && res.push(r) }
148
- list = res } }
149
407
  // handle iterable promise...
150
408
  if(list instanceof IterablePromise){
151
409
  return this.__handle(list.__packed, handler, onerror) }
152
410
  // handle promise / async-iterator...
153
- // XXX ITER do we unwind the iterator here or wait to unwind later???
154
411
  if(typeof(list) == 'object'
155
412
  && Symbol.asyncIterator in list){
156
413
  return list
157
414
  .iter(handler, onerror)
158
415
  .then(function(list){
159
416
  return that.__pack(list) }) }
160
- if(list instanceof Promise){
161
- return list
162
- .then(function(list){
163
- return that.__pack(list, handler, onerror) }) }
164
- // do the work...
165
- // NOTE: packing and handling are mixed here because it's faster
166
- // to do them both on a single list traverse...
167
- var handle = !!handler
168
- handler = handler
169
- ?? function(elem){
170
- return [elem] }
171
- // XXX this repeats .__handle(..), need to unify...
172
- var stop = false
173
- var map = 'map'
174
- var pack = function(){
175
- return [list].flat()
176
- [map](function(elem){
177
- // XXX EXPEREMENTAL...
178
- return elem instanceof IterablePromise ?
179
- (elem.isSync() ?
180
- handler(elem.sync())
181
- // XXX need to handle this but keep it IterablePromise...
182
- : elem.iterthen(handler))
183
- : (elem instanceof SyncPromise
184
- && !(elem.sync() instanceof Promise)) ?
185
- handler(elem.sync())
186
- : elem && elem.then ?
187
- (handleSTOP ?
188
- // stoppable -- need to handle stop async...
189
- elem
190
- .then(function(res){
191
- return !stop ?
192
- handler(res)
193
- : [] })
194
- // NOTE: we are using .catch(..) here
195
- // instead of directly passing the
196
- // error handler to be able to catch
197
- // the STOP from the handler...
198
- .catch(handleSTOP)
199
- // non-stoppable...
200
- : elem.then(handler))
201
- : elem instanceof Array ?
202
- handler(elem)
203
- // NOTE: we keep things that do not need protecting
204
- // from .flat() as-is...
205
- : !handle ?
206
- elem
207
- : handler(elem) }) }
208
- // pack (stoppable)...
209
- if(!!this.constructor.STOP){
210
- map = 'smap'
211
- var handleSTOP = function(err){
212
- // handle stop...
213
- stop = err
214
- if(err === that.constructor.STOP
215
- || err instanceof that.constructor.STOP){
216
- return 'value' in err ?
217
- err.value
218
- : [] }
219
- throw err }
220
- try{
221
- return pack()
222
- }catch(err){
223
- return handleSTOP(err) } }
224
417
 
225
- // pack (non-stoppable)...
226
- return pack() },
227
- // transform/handle packed array (sync, but can return promises in the list)...
228
- __handle: function(list, handler=undefined, onerror=undefined){
229
- var that = this
230
- if(typeof(list) == 'function'){
231
- handler = list
232
- list = this.__packed }
233
- if(!handler){
234
- return list }
235
- // handle promise list...
236
- if(list instanceof Promise){
237
- return list.then(function(list){
238
- return that.__handle(list, handler, onerror) }) }
239
- // do the work...
240
- // NOTE: since each section of the packed .__array is the same
241
- // structure as the input we'll use .__pack(..) to handle
242
- // them, this also keeps all the handling code in one place.
243
- var map = !!this.constructor.STOP ?
244
- 'smap'
245
- : 'map'
246
- return list[map](function(elem){
247
- elem = elem instanceof Array
248
- || elem instanceof Promise ?
249
- that.__pack(elem, handler, onerror)
250
- : [handler(elem)]
251
- elem = elem instanceof Promise ?
252
- elem.then(function([e]){
253
- return e })
254
- : elem
255
- return elem })
256
- .flat() },
257
- /*/
258
- __pack: function(list, handler=undefined, onerror=undefined){
259
- var that = this
260
- // handle iterator...
261
- // XXX ITER do we unwind the iterator here or wait to unwind later???
262
- if(typeof(list) == 'object'
263
- && Symbol.iterator in list){
264
- if(!onerror){
265
- list = [...list]
266
- // handle errors in input generator...
267
- // NOTE: this does not offer the most control because semantically
268
- // we bust behave in the same manner as <generator>.iter(..)
269
- } else {
270
- var res = []
271
- try{
272
- for(var e of list){
273
- res.push(e) }
274
- }catch(err){
275
- var r = onerror(err)
276
- r !== undefined
277
- && res.push(r) }
278
- list = res } }
279
- // handle iterable promise...
280
- if(list instanceof IterablePromise){
281
- return this.__handle(list.__packed, handler, onerror) }
282
- // handle promise / async-iterator...
283
- // XXX ITER do we unwind the iterator here or wait to unwind later???
284
- if(typeof(list) == 'object'
285
- && Symbol.asyncIterator in list){
286
- return list
287
- .iter(handler, onerror)
288
- .then(function(list){
289
- return that.__pack(list) }) }
290
- if(list instanceof Promise){
291
- return list
292
- .then(function(list){
293
- return that.__pack(list, handler, onerror) }) }
294
- // pack...
295
- list = [list].flat()
296
- .map(function(elem){
297
- return elem instanceof Array ?
298
- [elem]
299
- : elem instanceof Promise ?
300
- elem.then(function(e){
301
- return [e] })
302
- : elem })
418
+ // do the packing...
419
+ var packed = module.packed.pack(list)
303
420
  // handle if needed...
304
421
  return handler ?
305
- this.__handle(list, handler, onerror)
306
- : list },
422
+ this.__handle(packed, handler, onerror)
423
+ : packed },
307
424
  // transform/handle packed array (sync, but can return promises in the list)...
308
- // XXX need a strict spec...
309
- __handle: function(list, handler=undefined, onerror=undefined){
425
+ __handle: function(packed, handler=undefined, onerror=undefined){
310
426
  var that = this
311
- if(typeof(list) == 'function'){
312
- handler = list
313
- list = this.__packed }
427
+ // do .__handle(<func>)
428
+ if(typeof(packed) == 'function'){
429
+ handler = packed
430
+ packed = this.__packed }
314
431
  if(!handler){
315
- return list }
316
- // handle promise list...
317
- if(list instanceof Promise){
318
- return list.then(function(list){
319
- return that.__handle(list, handler, onerror) }) }
320
- // do the work...
321
- list = list instanceof Array ?
322
- list
323
- : [list]
324
- var map = !!this.constructor.STOP ?
325
- 'smap'
326
- : 'map'
327
- var stop = false
328
- // XXX do we handle generators here???
329
- var each = function(elem){
330
- return elem instanceof Array ?
331
- elem
332
- .map(handler)
333
- .flat()
334
- : handler(elem) }
335
- return list
336
- [map](
337
- function(elem){
338
- // NOTE: we are calling .flat() on the result so we
339
- // need to keep a handled array as a single
340
- // element by wrapping the return of handled(..)...
341
- return elem instanceof IterablePromise ?
342
- (elem.isSync() ?
343
- each(elem.sync())
344
- : elem.iterthen(each))
345
- // sync sync promise...
346
- : (elem instanceof SyncPromise
347
- && !(elem.sync() instanceof Promise)) ?
348
- [each(elem.sync())]
349
- // promise / promise-like...
350
- : elem && elem.then ?
351
- // NOTE: when this is explicitly stopped we
352
- // do not call any more handlers after
353
- // STOP is thrown/returned...
354
- // NOTE: the promise protects this from .flat()
355
- elem.then(function(elem){
356
- return !stop ?
357
- each(elem)
358
- : [] })
359
- : elem instanceof Array ?
360
- [each(elem)]
361
- : each(elem) },
362
- // handle STOP...
363
- function(){
364
- stop = true })
365
- .flat() },
366
- //*/
367
- // XXX this should return IterablePromise if .__packed is partially sync (???)
432
+ return packed }
433
+ return module.packed.handle(packed, ...[...arguments].slice(1)) },
368
434
  // unpack array (sync/async)...
369
- __unpack: function(list){
370
- list = list
435
+ __unpack: function(packed){
436
+ // do .__unpack()
437
+ packed = packed
371
438
  ?? this.__packed
372
439
  // handle promise list...
373
- if(list instanceof IterablePromise){
374
- return list.__unpack() }
375
- if(list instanceof Promise){
376
- return list
377
- .then(this.__unpack.bind(this)) }
378
- var res = []
379
- for(var e of list){
380
- if(e instanceof IterablePromise){
381
- e = e.__unpack() }
382
- if(e instanceof SyncPromise){
383
- e = e.sync() }
384
- // give up on a sync solution...
385
- if(e instanceof Promise){
386
- // XXX can we return an IterablePromise???
387
- // XXX is there a more elegant way to do this???
388
- return Promise.all(list)
389
- .then(function(list){
390
- return list.flat() })
391
- .iter() }
392
- res.push(e) }
393
- return res.flat() },
394
-
440
+ if(packed instanceof IterablePromise){
441
+ return packed.__unpack() }
442
+ return module.packed.unpack(packed) },
443
+
444
+
395
445
  [Symbol.asyncIterator]: async function*(){
396
446
  var list = this.__packed
397
447
  if(list instanceof Promise){
@@ -415,22 +465,19 @@ object.Constructor('IterablePromise', Promise, {
415
465
  map: function(func){
416
466
  return this.constructor(this,
417
467
  function(e){
418
- var res = func(e)
419
- return res instanceof Promise ?
420
- res.then(function(e){
421
- return [e] })
422
- : [res] }) },
468
+ return [func(e)] }) },
423
469
  filter: function(func){
424
470
  return this.constructor(this,
425
471
  function(e){
426
472
  var res = func(e)
427
- var _filter = function(elem){
428
- return res ?
429
- [elem]
430
- : [] }
431
473
  return res instanceof Promise ?
432
- res.then(_filter)
433
- : _filter(e) }) },
474
+ res.then(function(res){
475
+ return res ?
476
+ [e]
477
+ : [] })
478
+ : res ?
479
+ [e]
480
+ : [] }) },
434
481
  // NOTE: this does not return an iterable promise as we can't know
435
482
  // what the user reduces to...
436
483
  // NOTE: the items can be handled out of order because the nested
@@ -756,6 +803,7 @@ object.Constructor('IterablePromise', Promise, {
756
803
  // NOTE: if 'types/Array' is imported this will support throwing STOP,
757
804
  // for more info see notes for .__pack(..)
758
805
  // XXX EXPEREMENTAL: STOP...
806
+ // XXX use selfResolve(..)
759
807
  __new__: function(_, list, handler=undefined, onerror=undefined){
760
808
  // instance...
761
809
  var promise
@@ -803,12 +851,16 @@ object.Constructor('IterablePromise', Promise, {
803
851
  list instanceof Promise ?
804
852
  list.then(function(list){
805
853
  obj.__packed = list })
854
+ /*/ XXX use selfResolve(..)
855
+ : selfResolve(list) }
856
+ /*/
806
857
  : list.forEach(function(elem, i){
807
858
  elem instanceof Promise
808
859
  && elem.then(function(elem){
809
860
  lst = obj.__packed.slice()
810
861
  lst[i] = elem
811
862
  obj.__packed = lst }) }) }
863
+ //*/
812
864
  return obj },
813
865
  })
814
866
 
@@ -816,6 +868,7 @@ object.Constructor('IterablePromise', Promise, {
816
868
 
817
869
  //---------------------------------------------------------------------
818
870
  // XXX EXPEREMENTAL/HACK...
871
+ // Sequential iterable promise...
819
872
 
820
873
  // This like IterablePromise but guarantees handler execution in order
821
874
  // element occurrence.
@@ -827,7 +880,7 @@ object.Constructor('IterablePromise', Promise, {
827
880
  // Promise.iter([ .. ]).iter(func)
828
881
  // - func per element
829
882
  // - func is called when an element is resolved/ready
830
- // in any order
883
+ // in order of resolution/readiness
831
884
  // Promise.seqiter([ .. ]).iter(func)
832
885
  // - func per element
833
886
  // - func is called when an element is resolved/ready
@@ -836,6 +889,13 @@ object.Constructor('IterablePromise', Promise, {
836
889
  // NOTE: that here a promise will block handling of later promises even
837
890
  // if they are resolved before it.
838
891
  //
892
+ // XXX is this correct???
893
+ // > g = function*(){ yield* [1,2,3] }
894
+ // // XXX should this expand the generator???
895
+ // > await Promise.seqiter([0, g()])
896
+ // -> [ 0, Object [Generator] {} ]
897
+ // > await Promise.seqiter([0, g()]).flat()
898
+ // -> [ 0, 1, 2, 3 ]
839
899
  // XXX check if this behaves correctly (call order) on concatenation and
840
900
  // other methods...
841
901
  // XXX not sure if this is a viable strategy....
@@ -848,13 +908,14 @@ object.Constructor('IterableSequentialPromise', IterablePromise, {
848
908
  var repack = function(list){
849
909
  var res = []
850
910
  for(var [i, e] of list.entries()){
851
- // XXX check for .then(..) instead???
852
- //if(e instanceof Promise
853
911
  if(e.then
854
912
  // skip last promise -- nothing to wrap...
855
913
  && i < list.length-1){
856
914
  res.push(e
857
915
  .then(function(e){
916
+ // NOTE: this does not call any handlers, thus
917
+ // there should be no risk of out of order
918
+ // handler execution....
858
919
  return seqiter(
859
920
  [e, ...list.slice(i+1)])
860
921
  .flat() }))
@@ -867,14 +928,12 @@ object.Constructor('IterableSequentialPromise', IterablePromise, {
867
928
  list = list instanceof SyncPromise ?
868
929
  list.sync()
869
930
  : list
931
+ // repack...
870
932
  list = list instanceof Array ?
871
933
  repack(list)
872
- // XXX check for .then(..) instead???
873
- //: list instanceof Promise ?
874
934
  : list.then ?
875
935
  list.then(repack)
876
936
  : list
877
-
878
937
  return handler ?
879
938
  this.__handle(list, handler, onerror)
880
939
  : list },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ig-types",
3
- "version": "6.24.16",
3
+ "version": "6.24.20",
4
4
  "description": "Generic JavaScript types and type extensions...",
5
5
  "main": "main.js",
6
6
  "scripts": {
package/runner.js CHANGED
@@ -12,6 +12,8 @@
12
12
  * - ...
13
13
  * would be nice to make the task just a slightly extended or better
14
14
  * defined function/generator, ideally to make them interchangable...
15
+ * XXX BUG: there seems to be an odd bug somplace here where each item
16
+ * gets processed once per "thread"... (ImageGrid.Viewer export)
15
17
  *
16
18
  **********************************************/ /* c8 ignore next 2 */
17
19
  ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
@@ -360,6 +362,7 @@ object.Constructor('Queue', Array, {
360
362
  // report...
361
363
  that.trigger('taskCompleted', task, res) }
362
364
  var fail = {doc: 'fail runningDone(..)'}
365
+ // XXX should this also remove .__running when empty???
363
366
  var runningDone = function(mode){
364
367
  running.splice(0, running.length,
365
368
  // NOTE: there can be multiple occurrences of res...
@@ -409,7 +412,7 @@ object.Constructor('Queue', Array, {
409
412
  // collect results...
410
413
  this.collect_results
411
414
  && res !== module.SKIP
412
- && (this.__results = this.__results || []).push(res)
415
+ && (this.__results = this.__results ?? []).push(res)
413
416
 
414
417
  // handle task results...
415
418
  //
@@ -516,6 +519,9 @@ object.Constructor('Queue', Array, {
516
519
  //
517
520
  // NOTE: this helps get around the argument number limitation in JS...
518
521
  add: function(tasks){
522
+ tasks = tasks instanceof Array ?
523
+ tasks
524
+ : [tasks]
519
525
  // handle too large a number of args...
520
526
  var MAX_ARGS = 10000
521
527
  if(tasks.length > MAX_ARGS){
@@ -568,37 +574,51 @@ module.FinalizableQueue =
568
574
  object.Constructor('FinalizableQueue', Queue, {
569
575
  auto_stop: true,
570
576
 
577
+ // not for direct use...
578
+ __freeze: function(){
579
+ Object.freeze(this)
580
+ // XXX do we remove/freeze .__running???
581
+ this.__results
582
+ && Object.freeze(this.__results)
583
+ return this },
584
+
571
585
  __onempty__: function(){
572
586
  return this.trigger('done') },
573
587
 
574
588
  done: events.Event('done', function(handle){
575
589
  // abort only once...
576
- if(this.state == 'aborted' || this.state == 'done'){
590
+ if(this.state == 'aborted'
591
+ || this.state == 'done'){
577
592
  return handle(false) }
578
593
  this.__state = 'done'
579
- Object.freeze(this) }),
594
+ this.__freeze() }),
580
595
  abort: events.Event('abort', function(handle){
581
596
  // abort only once...
582
- if(this.state == 'aborted' || this.state == 'done'){
597
+ if(this.state == 'aborted'
598
+ || this.state == 'done'){
583
599
  return handle(false) }
584
600
  this.__state = 'aborted'
585
- Object.freeze(this) }),
601
+ this.__freeze() }),
586
602
 
587
603
  // NOTE: each handler will get called once when the next time the
588
604
  // queue is emptied...
589
605
  promise: function(){
606
+ return this.__promise },
607
+ then: function(onresolve, onreject){
608
+ return this.promise().then(...arguments) },
609
+ catch: function(onreject){
610
+ return this.promise().catch(...arguments) },
611
+
612
+ __init__: function(options){
590
613
  var that = this
591
- return new Promise(function(resolve, reject){
614
+ this.__promise = new Promise(function(resolve, reject){
592
615
  that
593
616
  .one('done', function(){
594
617
  resolve(...(that.collect_results ?
595
- [that.__results || []]
618
+ [that.__results ?? []]
596
619
  : [])) })
597
- .one('abort', reject) }) },
598
- then: function(onresolve, onreject){
599
- return this.promise().then(...arguments) },
600
- catch: function(onreject){
601
- return this.promise().catch(...arguments) },
620
+ .one('abort', reject) })
621
+ return object.parentCall(FinalizableQueue.prototype.__init__, this, ...arguments) },
602
622
  })
603
623
 
604
624
 
package/test.js CHANGED
@@ -112,9 +112,9 @@ var cases = test.Cases({
112
112
  },
113
113
 
114
114
  IterablePromise: test.TestSet(function(){
115
- var create = function(assert, value){
115
+ var create = function(assert, value, expected){
116
116
  return {
117
- input: value,
117
+ input: expected ?? value,
118
118
  output: assert(Promise.iter(value), 'Promise.iter(', value, ')'),
119
119
  } }
120
120
 
@@ -136,29 +136,53 @@ var cases = test.Cases({
136
136
  array_mixed: function(assert){
137
137
  return create(assert, [1, Promise.resolve(2), 3]) },
138
138
  nested_array_mixed: function(assert){
139
- return create(assert, [
140
- 1,
141
- Promise.resolve(2),
142
- [3],
143
- Promise.resolve([4]),
144
- ]) },
139
+ return create(assert,
140
+ [1, Promise.resolve(2), [3], Promise.resolve([4])],
141
+ [1, 2, [3], [4]]) },
145
142
  promise_array_mixed: function(assert){
146
- return create(assert, Promise.resolve([1, Promise.resolve(2), 3])) },
143
+ return create(assert,
144
+ Promise.resolve([1, Promise.resolve(2), 3]),
145
+ [1, 2, 3]) },
147
146
  promise_nested_array_mixed: function(assert){
148
- return create(assert, Promise.resolve([
149
- 1,
150
- Promise.resolve(2),
151
- [3],
152
- Promise.resolve([4]),
153
- ])) },
147
+ return create(assert,
148
+ Promise.resolve([
149
+ 1,
150
+ Promise.resolve(2),
151
+ [3],
152
+ Promise.resolve([4]),
153
+ ]),
154
+ [1, 2, [3], [4]]) },
154
155
  })
155
156
  this.Modifier({
156
157
  nest: function(assert, setup){
157
158
  setup.output = Promise.iter(setup.output)
158
159
  return setup },
159
- iter: function(assert, setup){
160
+ iter_noargs: function(assert, setup){
160
161
  setup.output = setup.output.iter()
161
162
  return setup },
163
+ iter_asis: function(assert, setup){
164
+ setup.output = setup.output
165
+ .iter(function(e){
166
+ return [e] })
167
+ return setup },
168
+ iter_clear: function(assert, setup){
169
+ return {
170
+ input: [],
171
+ output: setup.output
172
+ .iter(function(e){
173
+ return [] }),
174
+ } },
175
+ /* XXX does not account for promises as input...
176
+ iter_flat: async function(assert, setup){
177
+ return {
178
+ input: setup.input instanceof Array ?
179
+ (await Promise.all(setup.input)).flat()
180
+ : [await setup.input],
181
+ output: setup.output
182
+ .iter(function(e){
183
+ return e }),
184
+ } },
185
+ //*/
162
186
 
163
187
  map_asis: function(assert, setup){
164
188
  setup.output = setup.output
@@ -182,6 +206,19 @@ var cases = test.Cases({
182
206
  output: setup.output
183
207
  .filter(function(e){ return false }),
184
208
  } },
209
+ filter_promise_all: function(assert, setup){
210
+ setup.output = setup.output
211
+ .filter(function(e){
212
+ return Promise.resolve(true) })
213
+ return setup },
214
+ filter_promise_none: function(assert, setup){
215
+ return {
216
+ input: [],
217
+ output: setup.output
218
+ .filter(function(e){
219
+ return Promise.resolve(false) }),
220
+ } },
221
+ //*/
185
222
 
186
223
  /* XXX need tuning...
187
224
  concat_basic: function(assert, {input, output}){
@@ -300,6 +337,118 @@ var cases = test.Cases({
300
337
  [1,2,3],
301
338
  'flat unpack', meth)
302
339
  }
340
+
341
+ var order = []
342
+ await Promise.seqiter([
343
+ 1,
344
+ Promise.resolve(2),
345
+ Promise.all([3,4]),
346
+ Promise.seqiter([5]),
347
+ 6,
348
+ ])
349
+ .flat()
350
+ .map(function(e){
351
+ order.push(e)
352
+ return e })
353
+ assert.array(
354
+ order,
355
+ [1,2,3,4,5,6],
356
+ 'Promise.seqiter(..) handle order')
357
+
358
+
359
+ var test_async_handler = async function(input, output, handler, msg){
360
+ // sanity check...
361
+ assert.array(
362
+ await Promise.iter(input, handler),
363
+ output,
364
+ msg+' (sanity check -- fix first)')
365
+ assert.array(
366
+ await Promise.iter(input,
367
+ function(e){
368
+ return Promise.resolve(handler(e)) }),
369
+ await Promise.iter(input, handler),
370
+ msg) }
371
+
372
+ await test_async_handler(
373
+ [
374
+ 1,
375
+ [2],
376
+ Promise.resolve(3),
377
+ Promise.resolve([4]),
378
+ ],
379
+ [],
380
+ function(e){
381
+ return [] },
382
+ 'handler returns promise (empty)')
383
+ await test_async_handler(
384
+ [
385
+ 1,
386
+ [2],
387
+ Promise.resolve(3),
388
+ Promise.resolve([4]),
389
+ ],
390
+ [ 'moo', 'moo', 'moo', 'moo' ],
391
+ function(e){
392
+ return ['moo'] },
393
+ 'handler returns promise (array)')
394
+ await test_async_handler(
395
+ [
396
+ 1,
397
+ [2],
398
+ Promise.resolve(3),
399
+ Promise.resolve([4]),
400
+ ],
401
+ [ ['moo'], ['moo'], ['moo'], ['moo'] ],
402
+ function(e){
403
+ return [['moo']] },
404
+ 'handler returns promise (array-array)')
405
+
406
+ // test STOP...
407
+ assert.array(
408
+ await Promise.iter([1,2,3,4], function(e){
409
+ if(e == 3){
410
+ throw Array.STOP }
411
+ return e }),
412
+ [1,2],
413
+ '.iter(..): STOP: basic')
414
+ assert.array(
415
+ await Promise.iter([1,2,Promise.resolve(3),4], function(e){
416
+ if(e == 3){
417
+ throw Array.STOP }
418
+ return e }),
419
+ // NOTE: 4 here is present as it was handled before the promise resolved...
420
+ [1,2,4],
421
+ '.iter(..): STOP: delayed')
422
+ assert.array(
423
+ await Promise.iter([1,2,Promise.resolve(3),4], function(e){
424
+ if(e == 3){
425
+ throw Array.STOP('stop') }
426
+ return e }),
427
+ // NOTE: 4 here is present as it was handled before the promise resolved...
428
+ [1,2,'stop',4],
429
+ '.iter(..): STOP(..): delayed')
430
+ assert.array(
431
+ await Promise.seqiter([1,2,3,4], function(e){
432
+ if(e == 3){
433
+ throw Array.STOP }
434
+ return e }),
435
+ [1,2],
436
+ '.seqiter(..): STOP: basic')
437
+ assert.array(
438
+ await Promise.seqiter([1,2,Promise.resolve(3),4], function(e){
439
+ if(e == 3){
440
+ throw Array.STOP }
441
+ return e }),
442
+ [1,2],
443
+ '.seqiter(..): STOP: delayed')
444
+ assert.array(
445
+ await Promise.seqiter([1,2,Promise.resolve(3),4], function(e){
446
+ if(e == 3){
447
+ throw Array.STOP('stop') }
448
+ return e }),
449
+ // NOTE: 4 here is present as it was handled before the promise resolved...
450
+ [1,2,'stop'],
451
+ '.seqiter(..): STOP(..): delayed')
303
452
  },
304
453
 
305
454
  // Date.js
@@ -557,8 +706,8 @@ Events.cases({
557
706
 
558
707
 
559
708
  // test event list...
560
- assert.array(obj.events, ['event', 'eventBlank'], '.events')
561
- assert.array(obj.eventful, ['bareEvent', 'bareEventBlank'], '.eventful')
709
+ assert.array(obj.events, ['eventBlank', 'event'], '.events')
710
+ assert.array(obj.eventful, ['bareEventBlank', 'bareEvent'], '.eventful')
562
711
 
563
712
  // bind...
564
713
  var bind = function(evt){