ig-types 6.13.7 → 6.15.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.
- package/Array.js +19 -13
- package/Promise.js +312 -96
- package/README.md +134 -0
- package/package.json +1 -1
package/Array.js
CHANGED
|
@@ -139,7 +139,8 @@ var makeChunkIter = function(iter, wrapper){
|
|
|
139
139
|
try {
|
|
140
140
|
// handle iteration...
|
|
141
141
|
res.push(
|
|
142
|
-
val = (chunk = chunks.shift())
|
|
142
|
+
val = (chunk = chunks.shift())
|
|
143
|
+
[iter](_wrapper, ...rest))
|
|
143
144
|
// handle chunk...
|
|
144
145
|
postChunk
|
|
145
146
|
&& postChunk.call(that,
|
|
@@ -319,9 +320,12 @@ object.Mixin('ArrayProtoMixin', 'soft', {
|
|
|
319
320
|
// NOTE: order is preserved...
|
|
320
321
|
unique: function(normalize){
|
|
321
322
|
return normalize ?
|
|
322
|
-
[...new Map(this
|
|
323
|
-
|
|
324
|
-
|
|
323
|
+
[...new Map(this
|
|
324
|
+
.map(function(e){
|
|
325
|
+
return [normalize(e), e] }))
|
|
326
|
+
.values()]
|
|
327
|
+
// NOTE: we are calling .compact() here to avoid creating
|
|
328
|
+
// undefined items from empty slots in sparse arrays...
|
|
325
329
|
: [...new Set(this.compact())] },
|
|
326
330
|
tailUnique: function(normalize){
|
|
327
331
|
return this
|
|
@@ -336,8 +340,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
|
|
|
336
340
|
// self to other (internal) while match compares two entities
|
|
337
341
|
// externally.
|
|
338
342
|
// XXX not sure if we need the destinction in name, will have to
|
|
339
|
-
// come back to this when refactoring diff.js -- all three
|
|
340
|
-
// to be similar...
|
|
343
|
+
// come back to this when refactoring diff.js -- all three
|
|
344
|
+
// have to be similar...
|
|
341
345
|
cmp: function(other){
|
|
342
346
|
if(this === other){
|
|
343
347
|
return true }
|
|
@@ -353,7 +357,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
|
|
|
353
357
|
// NOTE: this will ignore order and repeating elments...
|
|
354
358
|
setCmp: function(other){
|
|
355
359
|
return this === other
|
|
356
|
-
|| (new Set([...this, ...other])).length
|
|
360
|
+
|| (new Set([...this, ...other])).length
|
|
361
|
+
== (new Set(this)).length },
|
|
357
362
|
|
|
358
363
|
// Sort as the other array...
|
|
359
364
|
//
|
|
@@ -369,12 +374,13 @@ object.Mixin('ArrayProtoMixin', 'soft', {
|
|
|
369
374
|
// This will sort the intersecting items in the head keeping the rest
|
|
370
375
|
// of the items in the same relative order...
|
|
371
376
|
//
|
|
372
|
-
// NOTE: if an item is in the array multiple times only the first
|
|
373
|
-
// is used...
|
|
377
|
+
// NOTE: if an item is in the array multiple times only the first
|
|
378
|
+
// index is used...
|
|
374
379
|
//
|
|
375
380
|
// XXX should this extend/patch .sort(..)???
|
|
376
|
-
// ...currently do not see a clean way to do this without
|
|
377
|
-
// and replacing Array or directly re-wrapping
|
|
381
|
+
// ...currently do not see a clean way to do this without
|
|
382
|
+
// extending and replacing Array or directly re-wrapping
|
|
383
|
+
// .sort(..)...
|
|
378
384
|
sortAs: function(other, place='head'){
|
|
379
385
|
place = place == 'tail' ? -1 : 1
|
|
380
386
|
// NOTE: the memory overhead here is better than the time overhead
|
|
@@ -441,8 +447,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
|
|
|
441
447
|
|
|
442
448
|
// Convert an array to a map...
|
|
443
449
|
//
|
|
444
|
-
// This is similar to Array.prototype.toKeys(..) but does not
|
|
445
|
-
// value type to string.
|
|
450
|
+
// This is similar to Array.prototype.toKeys(..) but does not
|
|
451
|
+
// restrict value type to string.
|
|
446
452
|
//
|
|
447
453
|
// Format:
|
|
448
454
|
// Map([
|
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,62 @@ 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 EXPEREMENTAL: STOP...
|
|
52
|
+
// NOTE: the following can not be implemented here:
|
|
53
|
+
// .splice(..) - can't both modify and return
|
|
54
|
+
// a result...
|
|
55
|
+
// .pop() / .shift() - can't modify the promise, use
|
|
56
|
+
// .first() / .last() instead.
|
|
57
|
+
// [Symbol.iterator]() - needs to be sync and we can't
|
|
58
|
+
// know the number of elements to
|
|
59
|
+
// return promises before the whole
|
|
60
|
+
// iterable promise is resolved.
|
|
61
|
+
// NOTE: we are not using async/await here as we need to control the
|
|
62
|
+
// type of promise returned in cases where we know we are returning
|
|
63
|
+
// an array...
|
|
64
|
+
// NOTE: there is no point in implementing a 1:1 version of this that
|
|
65
|
+
// would not support element expansion/contraction as it would only
|
|
66
|
+
// simplify a couple of methods that are 1:1 (like .map(..) and
|
|
67
|
+
// .some(..)) while methods like .filter(..) will throw everything
|
|
68
|
+
// back to the complex IterablePromise...
|
|
69
|
+
//
|
|
70
|
+
// XXX how do we handle errors/rejections???
|
|
71
|
+
//
|
|
72
|
+
|
|
73
|
+
// XXX should these be exported???
|
|
74
|
+
var iterPromiseProxy =
|
|
75
|
+
//module.iterPromiseProxy =
|
|
76
|
+
function(name){
|
|
77
|
+
return function(...args){
|
|
78
|
+
return this.constructor(
|
|
79
|
+
this.then(function(lst){
|
|
80
|
+
return lst[name](...args) })) } }
|
|
81
|
+
var promiseProxy =
|
|
82
|
+
//module.promiseProxy =
|
|
83
|
+
function(name){
|
|
84
|
+
return async function(...args){
|
|
85
|
+
return (await this)[name](...args) } }
|
|
51
86
|
|
|
52
87
|
var IterablePromise =
|
|
53
88
|
module.IterablePromise =
|
|
54
89
|
object.Constructor('IterablePromise', Promise, {
|
|
55
|
-
// XXX
|
|
56
|
-
|
|
90
|
+
// XXX EXPEREMENTAL: STOP...
|
|
91
|
+
get STOP(){
|
|
92
|
+
return Array.STOP },
|
|
57
93
|
|
|
94
|
+
}, {
|
|
95
|
+
// packed array...
|
|
96
|
+
//
|
|
97
|
+
// Holds promise state.
|
|
58
98
|
//
|
|
59
99
|
// Format:
|
|
60
100
|
// [
|
|
@@ -64,16 +104,36 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
64
104
|
// ...
|
|
65
105
|
// ]
|
|
66
106
|
//
|
|
67
|
-
|
|
107
|
+
// This format has several useful features:
|
|
108
|
+
// - concatenating packed lists results in a packed list
|
|
109
|
+
// - adding an iterable promise (as-is) into a packed list results
|
|
110
|
+
// in a packed list
|
|
111
|
+
//
|
|
112
|
+
// NOTE: in general iterable promises are implicitly immutable, so
|
|
113
|
+
// it is not recomended to ever edit this in-place...
|
|
114
|
+
// NOTE: we are not isolating or "protecting" any internals to
|
|
115
|
+
// enable users to responsibly extend the code.
|
|
116
|
+
__packed: null,
|
|
68
117
|
|
|
69
|
-
// low-level .
|
|
118
|
+
// low-level .__packed handlers/helpers...
|
|
70
119
|
//
|
|
71
120
|
// NOTE: these can be useful for debugging and extending...
|
|
72
|
-
|
|
121
|
+
//
|
|
122
|
+
// pack and oprionally transform/handle an array (sync)...
|
|
123
|
+
//
|
|
124
|
+
// NOTE: if 'types/Array' is imported this will support throwing STOP
|
|
125
|
+
// from the handler.
|
|
126
|
+
// Due to the async nature of promises though the way stops are
|
|
127
|
+
// handled may be unpredictable -- the handlers can be run out
|
|
128
|
+
// of order, as the nested promises resolve and thus throwing
|
|
129
|
+
// stop will stop the handlers not yet run and not the next
|
|
130
|
+
// handlers in sequence.
|
|
131
|
+
// XXX EXPEREMENTAL: STOP...
|
|
132
|
+
__pack: function(list, handler=undefined){
|
|
73
133
|
var that = this
|
|
74
134
|
// handle iterable promise list...
|
|
75
135
|
if(list instanceof IterablePromise){
|
|
76
|
-
return this.__handle(list.
|
|
136
|
+
return this.__handle(list.__packed, handler) }
|
|
77
137
|
// handle promise list...
|
|
78
138
|
if(list instanceof Promise){
|
|
79
139
|
return list.then(function(list){
|
|
@@ -85,6 +145,57 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
85
145
|
handler = handler
|
|
86
146
|
?? function(elem){
|
|
87
147
|
return [elem] }
|
|
148
|
+
|
|
149
|
+
//* XXX EXPEREMENTAL: STOP...
|
|
150
|
+
var stoppable = false
|
|
151
|
+
var stop = false
|
|
152
|
+
var map = 'map'
|
|
153
|
+
var pack = function(){
|
|
154
|
+
return [list].flat()
|
|
155
|
+
[map](function(elem){
|
|
156
|
+
return elem && elem.then ?
|
|
157
|
+
(stoppable ?
|
|
158
|
+
// stoppable -- need to handle stop async...
|
|
159
|
+
elem
|
|
160
|
+
.then(function(res){
|
|
161
|
+
return !stop ?
|
|
162
|
+
handler(res)
|
|
163
|
+
: [] })
|
|
164
|
+
// NOTE: we are using .catch(..) here
|
|
165
|
+
// instead of directly passing the
|
|
166
|
+
// error handler to be able to catch
|
|
167
|
+
// the STOP from the handler...
|
|
168
|
+
.catch(handleSTOP)
|
|
169
|
+
// non-stoppable...
|
|
170
|
+
: elem.then(handler))
|
|
171
|
+
: elem instanceof Array ?
|
|
172
|
+
handler(elem)
|
|
173
|
+
// NOTE: we keep things that do not need protecting
|
|
174
|
+
// from .flat() as-is...
|
|
175
|
+
: !handle ?
|
|
176
|
+
elem
|
|
177
|
+
: handler(elem) }) }
|
|
178
|
+
|
|
179
|
+
// pack (stoppable)...
|
|
180
|
+
if(!!this.constructor.STOP){
|
|
181
|
+
stoppable = true
|
|
182
|
+
map = 'smap'
|
|
183
|
+
var handleSTOP = function(err){
|
|
184
|
+
stop = err
|
|
185
|
+
if(err === that.constructor.STOP
|
|
186
|
+
|| err instanceof that.constructor.STOP){
|
|
187
|
+
return 'value' in err ?
|
|
188
|
+
err.value
|
|
189
|
+
: [] }
|
|
190
|
+
throw err }
|
|
191
|
+
try{
|
|
192
|
+
return pack()
|
|
193
|
+
}catch(err){
|
|
194
|
+
return handleSTOP(err) } }
|
|
195
|
+
|
|
196
|
+
// pack (non-stoppable)...
|
|
197
|
+
return pack() },
|
|
198
|
+
/*/
|
|
88
199
|
return [list].flat()
|
|
89
200
|
.map(function(elem){
|
|
90
201
|
return elem && elem.then ?
|
|
@@ -97,11 +208,13 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
97
208
|
: !handle ?
|
|
98
209
|
elem
|
|
99
210
|
: handler(elem) }) },
|
|
100
|
-
|
|
211
|
+
//*/
|
|
212
|
+
// transform/handle packed array (sync)...
|
|
213
|
+
__handle: function(list, handler=undefined){
|
|
101
214
|
var that = this
|
|
102
215
|
if(typeof(list) == 'function'){
|
|
103
216
|
handler = list
|
|
104
|
-
list = this.
|
|
217
|
+
list = this.__packed }
|
|
105
218
|
if(!handler){
|
|
106
219
|
return list }
|
|
107
220
|
// handle promise list...
|
|
@@ -112,7 +225,14 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
112
225
|
// NOTE: since each section of the packed .__array is the same
|
|
113
226
|
// structure as the input we'll use .__pack(..) to handle
|
|
114
227
|
// them, this also keeps all the handling code in one place.
|
|
228
|
+
//* XXX EXPEREMENTAL: STOP...
|
|
229
|
+
var map = !!this.constructor.STOP ?
|
|
230
|
+
'smap'
|
|
231
|
+
: 'map'
|
|
232
|
+
return list[map](function(elem){
|
|
233
|
+
/*/
|
|
115
234
|
return list.map(function(elem){
|
|
235
|
+
//*/
|
|
116
236
|
return elem instanceof Array ?
|
|
117
237
|
that.__pack(elem, handler)
|
|
118
238
|
: elem instanceof Promise ?
|
|
@@ -121,48 +241,28 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
121
241
|
return elem.flat() })
|
|
122
242
|
: [handler(elem)] })
|
|
123
243
|
.flat() },
|
|
124
|
-
|
|
244
|
+
// unpack array (async)...
|
|
245
|
+
__unpack: async function(list){
|
|
125
246
|
list = list
|
|
126
|
-
?? this.
|
|
247
|
+
?? this.__packed
|
|
127
248
|
// handle promise list...
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return Promise.all(list)
|
|
134
|
-
.then(function(list){
|
|
135
|
-
return list.flat() }) },
|
|
249
|
+
return list instanceof Promise ?
|
|
250
|
+
this.__unpack(await list)
|
|
251
|
+
// do the work...
|
|
252
|
+
: (await Promise.all(list))
|
|
253
|
+
.flat() },
|
|
136
254
|
|
|
137
255
|
|
|
138
256
|
// iterator methods...
|
|
139
257
|
//
|
|
140
258
|
// These will return a new IterablePromise instance...
|
|
141
259
|
//
|
|
142
|
-
// When called from a resolved promise these will return a new
|
|
143
|
-
// resolved promise with updated values...
|
|
144
|
-
//
|
|
145
|
-
// When called from a rejected promise these will return a rejected
|
|
146
|
-
// with the same reason promise...
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
260
|
// NOTE: these are different to Array's equivalents in that the handler
|
|
150
261
|
// is called not in the order of the elements but rather in order
|
|
151
262
|
// of promise resolution...
|
|
152
263
|
// NOTE: index of items is unknowable because items can expand and
|
|
153
|
-
// contract depending on
|
|
264
|
+
// contract depending on handlers (e.g. .filter(..) can remove
|
|
154
265
|
// items)...
|
|
155
|
-
// This the following can not be implemented here:
|
|
156
|
-
// .slice(..)
|
|
157
|
-
// .splice(..)
|
|
158
|
-
// .values() / .keys()
|
|
159
|
-
// .at(..)
|
|
160
|
-
// [Symbol.iterator]() - needs to be sync...
|
|
161
|
-
// The followng methods are questionable:
|
|
162
|
-
// .indexOf(..)
|
|
163
|
-
// .includes(..)
|
|
164
|
-
// .some(..) / .every(..)
|
|
165
|
-
// .sort(..)
|
|
166
266
|
map: function(func){
|
|
167
267
|
return this.constructor(this,
|
|
168
268
|
function(e){
|
|
@@ -184,11 +284,11 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
184
284
|
: _filter(e) }) },
|
|
185
285
|
// NOTE: this does not return an iterable promise as we can't know
|
|
186
286
|
// what the user reduces to...
|
|
187
|
-
// XXX we could look at the initial state though...
|
|
188
287
|
// NOTE: the items can be handled out of order because the nested
|
|
189
288
|
// promises can resolve in any order...
|
|
190
289
|
// NOTE: since order of execution can not be guaranteed there is no
|
|
191
|
-
// point in implementing .reduceRight(..)
|
|
290
|
+
// point in implementing .reduceRight(..) in the same way
|
|
291
|
+
// (see below)...
|
|
192
292
|
reduce: function(func, res){
|
|
193
293
|
return this.constructor(this,
|
|
194
294
|
function(e){
|
|
@@ -196,6 +296,7 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
196
296
|
return [] })
|
|
197
297
|
.then(function(){
|
|
198
298
|
return res }) },
|
|
299
|
+
|
|
199
300
|
flat: function(depth=1){
|
|
200
301
|
return this.constructor(this,
|
|
201
302
|
function(e){
|
|
@@ -207,7 +308,7 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
207
308
|
e
|
|
208
309
|
: [e] }) },
|
|
209
310
|
reverse: function(){
|
|
210
|
-
var lst = this.
|
|
311
|
+
var lst = this.__packed
|
|
211
312
|
return this.constructor(
|
|
212
313
|
lst instanceof Promise ?
|
|
213
314
|
lst.then(function(elems){
|
|
@@ -252,15 +353,120 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
252
353
|
return this.constructor([elem])
|
|
253
354
|
.concat(this) },
|
|
254
355
|
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
//
|
|
356
|
+
// proxy methods...
|
|
357
|
+
//
|
|
358
|
+
// These require the whole promise to resolve to trigger.
|
|
359
|
+
//
|
|
360
|
+
// An exception to this would be .at(0)/.first() and .at(-1)/.last()
|
|
361
|
+
// that can get the target element if it's accessible.
|
|
362
|
+
//
|
|
363
|
+
// NOTE: methods that are guaranteed to return an array will return
|
|
364
|
+
// an iterable promise (created with iterPromiseProxy(..))...
|
|
365
|
+
//
|
|
366
|
+
at: async function(i){
|
|
367
|
+
var list = this.__packed
|
|
368
|
+
return ((i != 0 && i != -1)
|
|
369
|
+
|| list instanceof Promise
|
|
370
|
+
|| list.at(i) instanceof Promise) ?
|
|
371
|
+
(await this).at(i)
|
|
372
|
+
// NOTE: we can only reason about first/last explicit elements,
|
|
373
|
+
// anything else is non-deterministic...
|
|
374
|
+
: list.at(i) instanceof Promise ?
|
|
375
|
+
[await list.at(i)].flat().at(i)
|
|
376
|
+
: list.at(i) instanceof Array ?
|
|
377
|
+
list.at(i).at(i)
|
|
378
|
+
: list.at(i) },
|
|
379
|
+
first: function(){
|
|
380
|
+
return this.at(0) },
|
|
381
|
+
last: function(){
|
|
382
|
+
return this.at(-1) },
|
|
262
383
|
|
|
384
|
+
// NOTE: unlike .reduce(..) this needs the parent fully resolved
|
|
385
|
+
// to be able to iterate from the end.
|
|
386
|
+
// XXX is it faster to do .reverse().reduce(..) ???
|
|
387
|
+
// XXX ???
|
|
388
|
+
reduceRight: promiseProxy('reduceRight'),
|
|
389
|
+
|
|
390
|
+
// NOTE: there is no way we can do a sync generator returning
|
|
391
|
+
// promises for values because any promise in .__packed makes
|
|
392
|
+
// the value count/index non-deterministic...
|
|
393
|
+
sort: iterPromiseProxy('sort'),
|
|
394
|
+
slice: iterPromiseProxy('slice'),
|
|
395
|
+
|
|
396
|
+
entries: iterPromiseProxy('entries'),
|
|
397
|
+
keys: iterPromiseProxy('keys'),
|
|
398
|
+
values: iterPromiseProxy('values'),
|
|
399
|
+
|
|
400
|
+
indexOf: promiseProxy('indexOf'),
|
|
401
|
+
lastIndexOf: promiseProxy('lastIndexOf'),
|
|
402
|
+
includes: promiseProxy('includes'),
|
|
263
403
|
|
|
404
|
+
//
|
|
405
|
+
// .find(<func>)
|
|
406
|
+
// .find(<func>, 'value')
|
|
407
|
+
// -> <promise>(<value>)
|
|
408
|
+
//
|
|
409
|
+
// .find(<func>, 'result')
|
|
410
|
+
// -> <promise>(<result>)
|
|
411
|
+
//
|
|
412
|
+
// .find(<func>, 'bool')
|
|
413
|
+
// -> <promise>(<bool>)
|
|
414
|
+
//
|
|
415
|
+
// NOTE: this is slightly different to Array's .find(..) in that it
|
|
416
|
+
// accepts the result value enabling returning both the value
|
|
417
|
+
// itself ('value', default), the test function's result
|
|
418
|
+
// ('result') or true/false ('bool') -- this is added to be
|
|
419
|
+
// able to distinguish between the undefined as a stored value
|
|
420
|
+
// and undefined as a "nothing found" result.
|
|
421
|
+
// NOTE: I do not get how essentially identical methods .some(..)
|
|
422
|
+
// and .find(..) got added to JS's Array...
|
|
423
|
+
// the only benefit is that .some(..) handles undefined values
|
|
424
|
+
// stored in the array better...
|
|
425
|
+
// NOTE: this will return the result as soon as it's available but
|
|
426
|
+
// it will not stop the created but unresolved at the time
|
|
427
|
+
// promises from executing, this is both good and bad:
|
|
428
|
+
// + it will not break other clients waiting for promises
|
|
429
|
+
// to resolve...
|
|
430
|
+
// - if no clients are available this can lead to wasted
|
|
431
|
+
// CPU time...
|
|
432
|
+
find: async function(func, result='value'){
|
|
433
|
+
var that = this
|
|
434
|
+
// NOTE: not using pure await here as this is simpler to actually
|
|
435
|
+
// control the moment the resulting promise resolves without
|
|
436
|
+
// the need for juggling state...
|
|
437
|
+
return new Promise(function(resolve, reject){
|
|
438
|
+
var resolved = false
|
|
439
|
+
that.map(function(elem){
|
|
440
|
+
var res = func(elem)
|
|
441
|
+
if(res){
|
|
442
|
+
resolved = true
|
|
443
|
+
resolve(
|
|
444
|
+
result == 'bool' ?
|
|
445
|
+
true
|
|
446
|
+
: result == 'result' ?
|
|
447
|
+
res
|
|
448
|
+
: elem)
|
|
449
|
+
// XXX EXPEREMENTAL: STOP...
|
|
450
|
+
// NOTE: we do not need to throw STOP here
|
|
451
|
+
// but it can prevent some overhead...
|
|
452
|
+
if(that.constructor.STOP){
|
|
453
|
+
throw that.constructor.STOP } } })
|
|
454
|
+
.then(function(){
|
|
455
|
+
resolved
|
|
456
|
+
|| resolve(
|
|
457
|
+
result == 'bool' ?
|
|
458
|
+
false
|
|
459
|
+
: undefined) }) }) },
|
|
460
|
+
findIndex: promiseProxy('findIndex'),
|
|
461
|
+
|
|
462
|
+
// NOTE: this is just a special-case of .find(..)
|
|
463
|
+
some: async function(func){
|
|
464
|
+
return this.find(func, 'bool') },
|
|
465
|
+
every: promiseProxy('every'),
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
// promise api...
|
|
469
|
+
//
|
|
264
470
|
// Overload .then(..), .catch(..) and .finally(..) to return a plain
|
|
265
471
|
// Promise instnace...
|
|
266
472
|
//
|
|
@@ -286,6 +492,13 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
286
492
|
: p },
|
|
287
493
|
|
|
288
494
|
|
|
495
|
+
// this is defined globally as Promise.prototype.iter(.,)
|
|
496
|
+
//
|
|
497
|
+
// for details see: PromiseMixin(..) below...
|
|
498
|
+
//iter: function(handler=undefined){ ... },
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
// constructor...
|
|
289
502
|
//
|
|
290
503
|
// Promise.iter([ .. ])
|
|
291
504
|
// -> iterable-promise
|
|
@@ -295,8 +508,9 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
295
508
|
//
|
|
296
509
|
//
|
|
297
510
|
// handler(e)
|
|
298
|
-
// -> [value]
|
|
511
|
+
// -> [value, ..]
|
|
299
512
|
// -> []
|
|
513
|
+
// -> <promise>
|
|
300
514
|
//
|
|
301
515
|
//
|
|
302
516
|
// NOTE: element index is unknowable until the full list is expanded
|
|
@@ -308,7 +522,7 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
308
522
|
//
|
|
309
523
|
// Special cases useful for extending this constructor...
|
|
310
524
|
//
|
|
311
|
-
// Set raw .
|
|
525
|
+
// Set raw .__packed without any pre-processing...
|
|
312
526
|
// Promise.iter([ .. ], 'raw')
|
|
313
527
|
// -> iterable-promise
|
|
314
528
|
//
|
|
@@ -317,18 +531,9 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
317
531
|
// -> iterable-promise
|
|
318
532
|
//
|
|
319
533
|
//
|
|
320
|
-
//
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
// needed...
|
|
324
|
-
// XXX would be nice to support throwing STOP...
|
|
325
|
-
// - this is more complicated than simply suing .smap(..) instead
|
|
326
|
-
// of .map(..) because the list can contain async promises...
|
|
327
|
-
// ...would need to wrap each .then(..) call in try-catch and
|
|
328
|
-
// manually handle the stop...
|
|
329
|
-
// - another issue here is that the stop would happen in order of
|
|
330
|
-
// execution and not order of elements...
|
|
331
|
-
// XXX how do we handle errors???
|
|
534
|
+
// NOTE: if 'types/Array' is imported this will support throwing STOP,
|
|
535
|
+
// for more info see notes for .__pack(..)
|
|
536
|
+
// XXX EXPEREMENTAL: STOP...
|
|
332
537
|
__new__: function(_, list, handler){
|
|
333
538
|
// instance...
|
|
334
539
|
var promise
|
|
@@ -344,14 +549,14 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
344
549
|
promise = {resolve, reject} }],
|
|
345
550
|
IterablePromise)
|
|
346
551
|
|
|
347
|
-
// new instance...
|
|
552
|
+
// populate new instance...
|
|
348
553
|
if(promise){
|
|
349
554
|
// handle/pack input data...
|
|
350
555
|
if(handler != 'raw'){
|
|
351
556
|
list = list instanceof IterablePromise ?
|
|
352
|
-
this.__handle(list.
|
|
557
|
+
this.__handle(list.__packed, handler)
|
|
353
558
|
: this.__pack(list, handler) }
|
|
354
|
-
Object.defineProperty(obj, '
|
|
559
|
+
Object.defineProperty(obj, '__packed', {
|
|
355
560
|
value: list,
|
|
356
561
|
enumerable: false,
|
|
357
562
|
})
|
|
@@ -414,13 +619,14 @@ object.Constructor('InteractivePromise', Promise, {
|
|
|
414
619
|
// register a handler...
|
|
415
620
|
} else {
|
|
416
621
|
var h = obj == null ?
|
|
417
|
-
// NOTE: we need to get the handlers from
|
|
418
|
-
// unless we are not
|
|
419
|
-
// bootstrap
|
|
420
|
-
//
|
|
421
|
-
//
|
|
422
|
-
//
|
|
423
|
-
//
|
|
622
|
+
// NOTE: we need to get the handlers from
|
|
623
|
+
// .__message_handlers unless we are not
|
|
624
|
+
// fully defined yet, then use the bootstrap
|
|
625
|
+
// container (handlers)...
|
|
626
|
+
// ...since we can call onmessage(..) while
|
|
627
|
+
// the promise is still defined there is no
|
|
628
|
+
// way to .send(..) until it returns a promise
|
|
629
|
+
// object, this races here are highly unlikely...
|
|
424
630
|
handlers
|
|
425
631
|
: (obj.__message_handlers =
|
|
426
632
|
obj.__message_handlers ?? [])
|
|
@@ -507,10 +713,13 @@ object.Constructor('CooperativePromise', Promise, {
|
|
|
507
713
|
var ProxyPromise =
|
|
508
714
|
module.ProxyPromise =
|
|
509
715
|
object.Constructor('ProxyPromise', Promise, {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
716
|
+
|
|
717
|
+
then: IterablePromise.prototype.then,
|
|
718
|
+
|
|
719
|
+
__new__: function(context, other, nooverride=false){
|
|
720
|
+
var proto = 'prototype' in other ?
|
|
721
|
+
other.prototype
|
|
722
|
+
: other
|
|
514
723
|
var obj = Reflect.construct(
|
|
515
724
|
ProxyPromise.__proto__,
|
|
516
725
|
[function(resolve, reject){
|
|
@@ -521,6 +730,9 @@ object.Constructor('ProxyPromise', Promise, {
|
|
|
521
730
|
// NOTE: we are not using object.deepKeys(..) here as we need
|
|
522
731
|
// the key origin not to trigger property getters...
|
|
523
732
|
var seen = new Set()
|
|
733
|
+
nooverride = nooverride instanceof Array ?
|
|
734
|
+
new Set(nooverride)
|
|
735
|
+
: nooverride
|
|
524
736
|
while(proto != null){
|
|
525
737
|
Object.entries(Object.getOwnPropertyDescriptors(proto))
|
|
526
738
|
.forEach(function([key, value]){
|
|
@@ -535,11 +747,15 @@ object.Constructor('ProxyPromise', Promise, {
|
|
|
535
747
|
&& Object.prototype.run === value.value)
|
|
536
748
|
&& !value.enumerable){
|
|
537
749
|
return }
|
|
750
|
+
// do not override existing methods...
|
|
751
|
+
if(nooverride === true ?
|
|
752
|
+
key in obj
|
|
753
|
+
: nooverride instanceof Set ?
|
|
754
|
+
nooverride.has(key)
|
|
755
|
+
: nooverride){
|
|
756
|
+
return }
|
|
538
757
|
// proxy...
|
|
539
|
-
obj[key] =
|
|
540
|
-
// XXX should we also .catch(..) here???
|
|
541
|
-
return context.then(function(res){
|
|
542
|
-
return res[key](...args) }) } })
|
|
758
|
+
obj[key] = promiseProxy(key) })
|
|
543
759
|
proto = proto.__proto__ }
|
|
544
760
|
return obj },
|
|
545
761
|
})
|
|
@@ -563,7 +779,7 @@ var PromiseProtoMixin =
|
|
|
563
779
|
module.PromiseProtoMixin =
|
|
564
780
|
object.Mixin('PromiseProtoMixin', 'soft', {
|
|
565
781
|
as: ProxyPromise,
|
|
566
|
-
iter: function(handler){
|
|
782
|
+
iter: function(handler=undefined){
|
|
567
783
|
return IterablePromise(this, handler) },
|
|
568
784
|
})
|
|
569
785
|
|
|
@@ -573,4 +789,4 @@ PromiseProtoMixin(Promise.prototype)
|
|
|
573
789
|
|
|
574
790
|
|
|
575
791
|
/**********************************************************************
|
|
576
|
-
* vim:set ts=4 sw=4 :
|
|
792
|
+
* vim:set ts=4 sw=4 nowrap : */ return module })
|
package/README.md
CHANGED
|
@@ -77,8 +77,14 @@ Library of JavaScript type extensions, types and utilities.
|
|
|
77
77
|
- [`<promise-iter>.reverse()`](#promise-iterreverse)
|
|
78
78
|
- [`<promise-iter>.concat(..)`](#promise-iterconcat)
|
|
79
79
|
- [`<promise-iter>.push(..)` / `<promise-iter>.unshift(..)`](#promise-iterpush--promise-iterunshift)
|
|
80
|
+
- [`<promise-iter>.at(..)` / `<promise-iter>.first()` / `<promise-iter>.last()`](#promise-iterat--promise-iterfirst--promise-iterlast)
|
|
81
|
+
- [`<promise-iter>.some(..)` / `<promise-iter>.find(..)`](#promise-itersome--promise-iterfind)
|
|
82
|
+
- [Array proxy methods returning `<promise-iter>`](#array-proxy-methods-returning-promise-iter)
|
|
83
|
+
- [Array proxy methods returning a `<promise>`](#array-proxy-methods-returning-a-promise)
|
|
80
84
|
- [`<promise-iter>.then(..)` / `<promise-iter>.catch(..)` / `<promise-iter>.finally(..)`](#promise-iterthen--promise-itercatch--promise-iterfinally)
|
|
85
|
+
- [`promise.IterablePromise.STOP` / `promise.IterablePromise.STOP(..)`](#promiseiterablepromisestop--promiseiterablepromisestop)
|
|
81
86
|
- [Advanced handler](#advanced-handler)
|
|
87
|
+
- [Stopping the iteration](#stopping-the-iteration)
|
|
82
88
|
- [Promise proxies](#promise-proxies)
|
|
83
89
|
- [`<promise>.as(..)`](#promiseas)
|
|
84
90
|
- [`<promise-proxy>.<method>(..)`](#promise-proxymethod)
|
|
@@ -1508,6 +1514,17 @@ promise, even if the original is resolved.
|
|
|
1508
1514
|
If all values are resolved the `<promise-iter>` will resolve on the next
|
|
1509
1515
|
execution frame.
|
|
1510
1516
|
|
|
1517
|
+
There are two types of iterator methods here, both are transparent but different
|
|
1518
|
+
in how they process values:
|
|
1519
|
+
- *Parallel methods*
|
|
1520
|
+
These handle elements as soon as they are available even if the parent promise
|
|
1521
|
+
is not yet resolved.
|
|
1522
|
+
<!-- XXX list -->
|
|
1523
|
+
- *Proxies*
|
|
1524
|
+
These methods simply wait for the main promise to resolve and then call the
|
|
1525
|
+
appropriate method on the result.
|
|
1526
|
+
<!-- XXX list + mark definitions as "(proxy)" -->
|
|
1527
|
+
|
|
1511
1528
|
<!--
|
|
1512
1529
|
XXX should we support generators as input?
|
|
1513
1530
|
...not sure about the control flow direction here, on one hand the generator
|
|
@@ -1652,6 +1669,95 @@ and [`<array>.unshift(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScr
|
|
|
1652
1669
|
see them for more info.
|
|
1653
1670
|
|
|
1654
1671
|
|
|
1672
|
+
#### `<promise-iter>.at(..)` / `<promise-iter>.first()` / `<promise-iter>.last()`
|
|
1673
|
+
|
|
1674
|
+
Proxies to the appropriate array methods with a special-case: when getting elements
|
|
1675
|
+
at positions `0` or `-1` (i.e. `.first()` / `.last()`) these _can_ resolve before the
|
|
1676
|
+
parent `<promise-iter>`.
|
|
1677
|
+
|
|
1678
|
+
XXX
|
|
1679
|
+
|
|
1680
|
+
#### `<promise-iter>.some(..)` / `<promise-iter>.find(..)`
|
|
1681
|
+
|
|
1682
|
+
```bnf
|
|
1683
|
+
<promise-iter>.some(<func>)
|
|
1684
|
+
-> <promise>
|
|
1685
|
+
|
|
1686
|
+
<promise-iter>.find(<func>)
|
|
1687
|
+
-> <promise>
|
|
1688
|
+
```
|
|
1689
|
+
|
|
1690
|
+
The main difference between `.some(..)` and `.find(..)` is in that the `<promise>`
|
|
1691
|
+
returned from the former will resolve to either `true` or `false`, and in the later
|
|
1692
|
+
to the found value or `undefined`.
|
|
1693
|
+
|
|
1694
|
+
`.find(..)` supports an additional argument that controls what returned `<promise>`
|
|
1695
|
+
is resolved to...
|
|
1696
|
+
|
|
1697
|
+
```bnf
|
|
1698
|
+
<promise-iter>.find(<func>)
|
|
1699
|
+
<promise-iter>.find(<func>, 'value')
|
|
1700
|
+
-> <promise>
|
|
1701
|
+
|
|
1702
|
+
<promise-iter>.find(<func>, 'bool')
|
|
1703
|
+
-> <promise>
|
|
1704
|
+
|
|
1705
|
+
<promise-iter>.find(<func>, 'result')
|
|
1706
|
+
-> <promise>
|
|
1707
|
+
```
|
|
1708
|
+
|
|
1709
|
+
- `value` (default)
|
|
1710
|
+
resolve to the stored value if found and `undefined` otherwise.
|
|
1711
|
+
- `bool`
|
|
1712
|
+
resolve to `true` if the value is found and `false` otherwise, this is how
|
|
1713
|
+
`.some(..)` is impelemnted.
|
|
1714
|
+
- `result`
|
|
1715
|
+
resolve to the return value of the test `<func>`.
|
|
1716
|
+
|
|
1717
|
+
These are similar to [`<array>.some(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
|
|
1718
|
+
and [`<array>.find(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
|
|
1719
|
+
see them for more info.
|
|
1720
|
+
|
|
1721
|
+
#### Array proxy methods returning `<promise-iter>`
|
|
1722
|
+
|
|
1723
|
+
- `<promise-iter>.sort(..)`
|
|
1724
|
+
- `<promise-iter>.slice(..)`
|
|
1725
|
+
- `<promise-iter>.entries()` / `<promise-iter>.keys()` / `<promise-iter>.values()`
|
|
1726
|
+
|
|
1727
|
+
These methods are proxies to the appropriate array methods.
|
|
1728
|
+
|
|
1729
|
+
```bnf
|
|
1730
|
+
<promise-iter>.<method>(..)
|
|
1731
|
+
-> <promise-iter>
|
|
1732
|
+
```
|
|
1733
|
+
|
|
1734
|
+
These methods need the parent `<promise-iter>` to resolve before resolving themselves.
|
|
1735
|
+
|
|
1736
|
+
XXX links...
|
|
1737
|
+
|
|
1738
|
+
|
|
1739
|
+
#### Array proxy methods returning a `<promise>`
|
|
1740
|
+
|
|
1741
|
+
- `<promise-iter>.indexOf(..)`
|
|
1742
|
+
- `<promise-iter>.includes(..)`
|
|
1743
|
+
- `<promise-iter>.every(..)`
|
|
1744
|
+
- `<promise-iter>.findIndex(..)`
|
|
1745
|
+
|
|
1746
|
+
These methods are proxies to the appropriate array methods.
|
|
1747
|
+
|
|
1748
|
+
```bnf
|
|
1749
|
+
<promise-iter>.<method>(..)
|
|
1750
|
+
-> <promise>
|
|
1751
|
+
```
|
|
1752
|
+
|
|
1753
|
+
These methods need the parent `<promise-iter>` to resolve before resolving themselves.
|
|
1754
|
+
|
|
1755
|
+
Since the equivalent array methods do not return iterables these will return a basic
|
|
1756
|
+
(non-iterable) `<promise>`.
|
|
1757
|
+
|
|
1758
|
+
XXX links...
|
|
1759
|
+
|
|
1760
|
+
|
|
1655
1761
|
#### `<promise-iter>.then(..)` / `<promise-iter>.catch(..)` / `<promise-iter>.finally(..)`
|
|
1656
1762
|
|
|
1657
1763
|
An extension to
|
|
@@ -1665,6 +1771,16 @@ this adds the ability to pass no arguments
|
|
|
1665
1771
|
This will return a generic promise wrapper passing through the results as-is. This
|
|
1666
1772
|
can be useful to hide the extended promise API from further code.
|
|
1667
1773
|
|
|
1774
|
+
#### `promise.IterablePromise.STOP` / `promise.IterablePromise.STOP(..)`
|
|
1775
|
+
|
|
1776
|
+
A special object that when thrown from a function/promise handler will stop
|
|
1777
|
+
further iteration.
|
|
1778
|
+
|
|
1779
|
+
This is `undefined` until the `ig-types/Array` module is loaded.
|
|
1780
|
+
|
|
1781
|
+
For more info see: [Stopping the iteration](#stopping-the-iteration) below, and
|
|
1782
|
+
[the 'Array' STOP section](#arraystop--arraystop)
|
|
1783
|
+
|
|
1668
1784
|
|
|
1669
1785
|
#### Advanced handler
|
|
1670
1786
|
|
|
@@ -1716,6 +1832,24 @@ var p = Promise.iter(
|
|
|
1716
1832
|
console.log(lst) }) // -> [2, 2, 4, 4, [5, 6]]
|
|
1717
1833
|
```
|
|
1718
1834
|
|
|
1835
|
+
#### Stopping the iteration
|
|
1836
|
+
|
|
1837
|
+
Like the [`Array`](#arraystop--arraystop) module, this support throwing `STOP` to
|
|
1838
|
+
stop iteration. As we uses [`.smap(..)`](#arraysmap--arraysfilter--arraysreduce--arraysforeach)
|
|
1839
|
+
stopping support is supported if `ig-types/Array` module is loaded.
|
|
1840
|
+
|
|
1841
|
+
```javascript
|
|
1842
|
+
require('ig-types/Array')
|
|
1843
|
+
```
|
|
1844
|
+
|
|
1845
|
+
This is also different semantically, as promise iteration can happen out of order,
|
|
1846
|
+
stopping affects the order of processing and not order of the input array with one exception: promises already created can not be stopped in `JavaScript`.
|
|
1847
|
+
|
|
1848
|
+
Any handler function passed to a `<promise-iter>` method can `throw` a STOP.
|
|
1849
|
+
|
|
1850
|
+
For more details see: [the 'Array' STOP section](#arraystop--arraystop)
|
|
1851
|
+
|
|
1852
|
+
|
|
1719
1853
|
|
|
1720
1854
|
### Promise proxies
|
|
1721
1855
|
|