ig-types 6.13.4 → 6.14.0

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 (3) hide show
  1. package/Promise.js +104 -87
  2. package/README.md +11 -0
  3. package/package.json +1 -1
package/Promise.js CHANGED
@@ -1,11 +1,9 @@
1
1
  /**********************************************************************
2
- *
3
- *
4
- *
5
2
  *
6
3
  * This defines the following extensions to Promise:
7
4
  *
8
5
  * Promise.iter(seq)
6
+ * <promise>.iter()
9
7
  * Iterable promise object.
10
8
  * Similar to Promise.all(..) but adds basic iterator/generator
11
9
  * API and will resolve the items as they are ready (resolved).
@@ -19,6 +17,11 @@
19
17
  * Exposes the API to resolve/reject the promise object
20
18
  * externally.
21
19
  *
20
+ * <promise>.as(obj)
21
+ * Promise proxy.
22
+ * Proxies the methods available from obj to promise value.
23
+ *
24
+ *
22
25
  *
23
26
  *
24
27
  **********************************************/ /* c8 ignore next 2 */
@@ -28,18 +31,6 @@
28
31
 
29
32
  var object = require('ig-object')
30
33
 
31
- // XXX required for STOP...
32
- //var generator = require('./generator')
33
-
34
-
35
- /*********************************************************************/
36
-
37
- /* XXX not used yet...
38
- // NOTE: this is used in a similar fashion to Python's StopIteration...
39
- var STOP =
40
- module.STOP =
41
- object.STOP
42
- //*/
43
34
 
44
35
 
45
36
  //---------------------------------------------------------------------
@@ -48,13 +39,38 @@ module.STOP =
48
39
  // Like Promise.all(..) but adds ability to iterate through results
49
40
  // via generators .map(..)/.reduce(..) and friends...
50
41
  //
42
+ // NOTE: it would be nice to support throwing STOP from the iterable
43
+ // promise but...
44
+ // - this is more complicated than simply using .smap(..) instead
45
+ // of .map(..) because the list can contain async promises...
46
+ // ...would need to wrap each .then(..) call in try-catch and
47
+ // manually handle the stop...
48
+ // ...then there's a question of derivative iterators etc.
49
+ // - another issue here is that the stop would happen in order of
50
+ // execution and not order of elements...
51
+ // XXX though stopping within a single level might be useful...
52
+ //
53
+
54
+ // NOTE: we are not using async/await here as we need to control the
55
+ // type of promise returned in cases where we know we are returning
56
+ // an array...
57
+ // XXX should these be exported???
58
+ var iterPromiseProxy =
59
+ //module.iterPromiseProxy =
60
+ function(name){
61
+ return function(...args){
62
+ return this.constructor(
63
+ this.then(function(lst){
64
+ return lst[name](...args) })) } }
65
+ var promiseProxy =
66
+ //module.promiseProxy =
67
+ function(name){
68
+ return async function(...args){
69
+ return (await this)[name](...args) } }
51
70
 
52
71
  var IterablePromise =
53
72
  module.IterablePromise =
54
73
  object.Constructor('IterablePromise', Promise, {
55
- // XXX
56
- //STOP: object.STOP,
57
-
58
74
  //
59
75
  // Format:
60
76
  // [
@@ -64,6 +80,13 @@ object.Constructor('IterablePromise', Promise, {
64
80
  // ...
65
81
  // ]
66
82
  //
83
+ // This format has several useful features:
84
+ // - concatenating packed list results in a packed list
85
+ // - adding an iterable promise (as-is) into a packed list results
86
+ // in a packed list
87
+ //
88
+ // NOTE: in general iterable promises are implicitly immutable, so
89
+ // it is not recomended to ever edit this inplace...
67
90
  __list: null,
68
91
 
69
92
  // low-level .__list handlers/helpers...
@@ -163,8 +186,7 @@ object.Constructor('IterablePromise', Promise, {
163
186
  // .includes(..)
164
187
  // .some(..) / .every(..)
165
188
  // .sort(..)
166
- //
167
- // XXX should these support STOP???
189
+ // XXX update this note...
168
190
  map: function(func){
169
191
  return this.constructor(this,
170
192
  function(e){
@@ -188,11 +210,9 @@ object.Constructor('IterablePromise', Promise, {
188
210
  // what the user reduces to...
189
211
  // XXX we could look at the initial state though...
190
212
  // NOTE: the items can be handled out of order because the nested
191
- // promises can resolve in any order.
192
- // XXX doc how to go around this...
213
+ // promises can resolve in any order...
193
214
  // NOTE: since order of execution can not be guaranteed there is no
194
215
  // point in implementing .reduceRight(..)
195
- // XXX should func be able to return a promise???
196
216
  reduce: function(func, res){
197
217
  return this.constructor(this,
198
218
  function(e){
@@ -200,14 +220,7 @@ object.Constructor('IterablePromise', Promise, {
200
220
  return [] })
201
221
  .then(function(){
202
222
  return res }) },
203
- /* // XXX since order of execution is not fixed there is no point in
204
- // adding this.
205
- reduceRight: function(func, res){
206
- return this
207
- .reverse()
208
- .reduce(...arguments)
209
- .reverse() },
210
- //*/
223
+
211
224
  flat: function(depth=1){
212
225
  return this.constructor(this,
213
226
  function(e){
@@ -246,21 +259,17 @@ object.Constructor('IterablePromise', Promise, {
246
259
  var cur = this.__pack(this)
247
260
  var other = this.__pack(other)
248
261
  return this.constructor(
249
- // NOTE: we are not using only the first branch to keep the
250
- // lists as exposed as possible thus avoiding blocking
251
- // until the whole ting is resolved...
262
+ // NOTE: we need to keep things as exposed as possible, this
263
+ // is why we're not blanketing all the cases with
264
+ // Promise.all(..)...
252
265
  (cur instanceof Promise
253
266
  && other instanceof Promise) ?
254
- Promise.all([cur, other])
255
- .then(function(list){
256
- return list[0].concat(list[1]) })
267
+ [cur, other]
257
268
  : cur instanceof Promise ?
258
- cur.then(function(list){
259
- return list.concat(other) })
269
+ [cur, ...other]
260
270
  : other instanceof Promise ?
261
- other.then(function(list){
262
- return cur.concat(list) })
263
- : cur.concat(other),
271
+ [...cur, other]
272
+ : [...cur, ...other],
264
273
  'raw') },
265
274
  push: function(elem){
266
275
  return this.concat([elem]) },
@@ -268,14 +277,54 @@ object.Constructor('IterablePromise', Promise, {
268
277
  return this.constructor([elem])
269
278
  .concat(this) },
270
279
 
271
- // XXX do we need these?
280
+ // XXX can we do these?
272
281
  // .pop()
273
282
  // .shift()
274
- // .first() / .last()
275
- // .at(..)
283
+ // .splice(..)
276
284
  // ...would be nice if these could stop everything that's not
277
- // needed to execute...
285
+ // needed to execute -- likely not possible (XXX)
286
+ // ...these need to somehow both return an element and affect
287
+ // the iterator (or return a new one) -- we can trivially do either
288
+ // one action within the spec but not both...
289
+
290
+ // NOTE: this can avoid waiting for the whole promise to resolve only
291
+ // for indexes 0 and -1, everything else is non-deterministic and
292
+ // we'll have to wait for the whole thing to resolve.
293
+ at: async function(i){
294
+ var list = this.__list
295
+ return ((i != 0 && i != -1)
296
+ || list instanceof Promise
297
+ || list.at(i) instanceof Promise) ?
298
+ (await this).at(i)
299
+ // NOTE: we can only reason about first/last explicit elements,
300
+ // anything else is non-deterministic...
301
+ : list.at(i) instanceof Promise ?
302
+ [await list.at(i)].flat().at(i)
303
+ : list.at(i) instanceof Array ?
304
+ list.at(i).at(i)
305
+ : list.at(i) },
306
+ first: function(){
307
+ return this.at(0) },
308
+ last: function(){
309
+ return this.at(-1) },
278
310
 
311
+ // NOTE: there is no way we can do a sync generator returning
312
+ // promises for values because any promise in .__list makes the
313
+ // value count/index non-deterministic...
314
+ sort: iterPromiseProxy('sort'),
315
+ slice: iterPromiseProxy('slice'),
316
+ entries: iterPromiseProxy('entries'),
317
+ keys: iterPromiseProxy('keys'),
318
+ // XXX we could possibly make this better via .map(..)
319
+ values: iterPromiseProxy('values'),
320
+
321
+ indexOf: promiseProxy('indexOf'),
322
+ includes: promiseProxy('includes'),
323
+
324
+ every: promiseProxy('every'),
325
+ // XXX this can be lazy...
326
+ some: promiseProxy('some'),
327
+
279
328
 
280
329
  // Overload .then(..), .catch(..) and .finally(..) to return a plain
281
330
  // Promise instnace...
@@ -311,18 +360,19 @@ object.Constructor('IterablePromise', Promise, {
311
360
  //
312
361
  //
313
362
  // handler(e)
314
- // -> [value]
363
+ // -> [value, ..]
315
364
  // -> []
365
+ // -> <promise>
316
366
  //
317
367
  //
318
- // NOTE: element index is unknowable untill the full list is expanded
368
+ // NOTE: element index is unknowable until the full list is expanded
319
369
  // as handler(..)'s return value can expand to any number of
320
370
  // items...
321
371
  // XXX we can make the index a promise, then if the client needs
322
372
  // the value they can wait for it...
323
373
  //
324
374
  //
325
- // Spectial cases usefull for extending this constructor...
375
+ // Special cases useful for extending this constructor...
326
376
  //
327
377
  // Set raw .__list without any pre-processing...
328
378
  // Promise.iter([ .. ], 'raw')
@@ -333,45 +383,18 @@ object.Constructor('IterablePromise', Promise, {
333
383
  // -> iterable-promise
334
384
  //
335
385
  //
336
- // XXX if list is an iterator, can we fill this async???
337
386
  // XXX iterator/generator as input:
338
387
  // - do we unwind here or externally?
339
388
  // ...feels like with the generator external unwinding is
340
389
  // needed...
341
- // XXX would be nice to support trowing STOP...
342
- // - this is more complicated than simply suing .smap(..) instead
343
- // of .map(..) because the list can contain async promises...
344
- // ...would need to wrap each .then(..) call in try-catch and
345
- // manually handle the stop...
346
- // - another issue here is that the stop would happen in order of
347
- // execution and not order of elements...
348
- // XXX DOC:
349
- // inputs:
350
- // - Chaining -- list instanceof IterablePromise
351
- // After all the promises resolve .flat() should
352
- // turn this into the input list.
353
- // For this to work we'll need to at least wrap all
354
- // arrays and promise results in arrays.
355
- // (currently each value is wrapped)
356
- // -> __list
357
- // - promise (value | array)
358
- // - array of:
359
- // - array
360
- // - value
361
- // - promise (value | array)
362
- // - New
363
- // - promise (value | array)
364
- // - value (non-array)
365
- // - array of:
366
- // - value
367
- // - promise (value)
390
+ // XXX how do we handle errors/rejections???
368
391
  __new__: function(_, list, handler){
369
392
  // instance...
370
393
  var promise
371
394
  var obj = Reflect.construct(
372
395
  IterablePromise.__proto__,
373
396
  [function(resolve, reject){
374
- // NOTE: this is here for Promise compatibilty...
397
+ // NOTE: this is here for Promise compatibility...
375
398
  if(typeof(list) == 'function'){
376
399
  return list.call(this, ...arguments) }
377
400
  // initial reject...
@@ -380,17 +403,17 @@ object.Constructor('IterablePromise', Promise, {
380
403
  promise = {resolve, reject} }],
381
404
  IterablePromise)
382
405
 
406
+ // populate new instance...
383
407
  if(promise){
408
+ // handle/pack input data...
384
409
  if(handler != 'raw'){
385
410
  list = list instanceof IterablePromise ?
386
411
  this.__handle(list.__list, handler)
387
412
  : this.__pack(list, handler) }
388
-
389
413
  Object.defineProperty(obj, '__list', {
390
414
  value: list,
391
415
  enumerable: false,
392
416
  })
393
-
394
417
  // handle promise state...
395
418
  this.__unpack(list)
396
419
  .then(function(list){
@@ -572,10 +595,7 @@ object.Constructor('ProxyPromise', Promise, {
572
595
  && !value.enumerable){
573
596
  return }
574
597
  // proxy...
575
- obj[key] = function(...args){
576
- // XXX should we also .catch(..) here???
577
- return context.then(function(res){
578
- return res[key](...args) }) } })
598
+ obj[key] = promiseProxy(key) })
579
599
  proto = proto.__proto__ }
580
600
  return obj },
581
601
  })
@@ -595,13 +615,10 @@ object.Mixin('PromiseMixin', 'soft', {
595
615
  PromiseMixin(Promise)
596
616
 
597
617
 
598
- // XXX EXPEREMENTAL...
599
618
  var PromiseProtoMixin =
600
619
  module.PromiseProtoMixin =
601
620
  object.Mixin('PromiseProtoMixin', 'soft', {
602
621
  as: ProxyPromise,
603
-
604
- // XXX
605
622
  iter: function(handler){
606
623
  return IterablePromise(this, handler) },
607
624
  })
package/README.md CHANGED
@@ -1508,6 +1508,17 @@ promise, even if the original is resolved.
1508
1508
  If all values are resolved the `<promise-iter>` will resolve on the next
1509
1509
  execution frame.
1510
1510
 
1511
+ There are two types of iterator methods here, both are transparent but different
1512
+ in how they process values:
1513
+ - *Parallel methods*
1514
+ These handle elements as soon as they are available even if the parent promise
1515
+ is not yet resolved.
1516
+ <!-- XXX list -->
1517
+ - *Proxies*
1518
+ These methods simply wait for the main promise to resolve and then call the
1519
+ appropriate method on the result.
1520
+ <!-- XXX list + mark definitions as "(proxy)" -->
1521
+
1511
1522
  <!--
1512
1523
  XXX should we support generators as input?
1513
1524
  ...not sure about the control flow direction here, on one hand the generator
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ig-types",
3
- "version": "6.13.4",
3
+ "version": "6.14.0",
4
4
  "description": "Generic JavaScript types and type extensions...",
5
5
  "main": "main.js",
6
6
  "scripts": {