@qooxdoo/framework 7.8.0 → 7.9.1

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 (41) hide show
  1. package/Manifest.json +2 -2
  2. package/lib/compiler/compile-info.json +72 -70
  3. package/lib/compiler/index.js +2255 -1385
  4. package/lib/resource/qx/tool/website/build/404.html +3 -25
  5. package/lib/resource/qx/tool/website/build/about.html +3 -25
  6. package/lib/resource/qx/tool/website/build/assets/common.js +20 -0
  7. package/lib/resource/qx/tool/website/build/diagnostics/dependson.html +3 -25
  8. package/lib/resource/qx/tool/website/build/diagnostics/requiredby.html +3 -22
  9. package/lib/resource/qx/tool/website/build/index.html +3 -25
  10. package/lib/resource/qx/tool/website/partials/footer.html +3 -21
  11. package/lib/resource/qx/tool/website/partials/head.html +0 -1
  12. package/package.json +2 -1
  13. package/source/class/qx/Bootstrap.js +6 -3
  14. package/source/class/qx/Promise.js +93 -6964
  15. package/source/class/qx/core/Environment.js +1 -0
  16. package/source/class/qx/data/marshal/Json.js +64 -11
  17. package/source/class/qx/event/handler/TouchCore.js +3 -1
  18. package/source/class/qx/lang/Type.js +36 -3
  19. package/source/class/qx/promise/BluebirdImpl.js +6918 -0
  20. package/source/class/qx/promise/NativeWrapper.js +738 -0
  21. package/source/class/qx/test/Promise.js +1145 -22
  22. package/source/class/qx/test/data/controller/List.js +6 -0
  23. package/source/class/qx/test/data/marshal/Json.js +29 -0
  24. package/source/class/qx/test/io/request/Xhr.js +16 -0
  25. package/source/class/qx/test/lang/Type.js +151 -0
  26. package/source/class/qx/theme/indigo/ColorDark.js +1 -1
  27. package/source/class/qx/ui/core/MPlacement.js +18 -7
  28. package/source/class/qx/ui/table/pane/Scroller.js +1 -1
  29. package/source/class/qx/util/ConcurrencyLimiter.js +78 -0
  30. package/source/resource/qx/tool/website/build/404.html +3 -25
  31. package/source/resource/qx/tool/website/build/about.html +3 -25
  32. package/source/resource/qx/tool/website/build/assets/common.js +20 -0
  33. package/source/resource/qx/tool/website/build/diagnostics/dependson.html +3 -25
  34. package/source/resource/qx/tool/website/build/diagnostics/requiredby.html +3 -22
  35. package/source/resource/qx/tool/website/build/index.html +3 -25
  36. package/source/resource/qx/tool/website/partials/footer.html +3 -21
  37. package/source/resource/qx/tool/website/partials/head.html +0 -1
  38. package/lib/resource/qx/tool/website/build/assets/bluebird.min.js +0 -4615
  39. package/lib/resource/qx/tool/website/src/assets/bluebird.min.js +0 -4615
  40. package/source/resource/qx/tool/website/build/assets/bluebird.min.js +0 -4615
  41. package/source/resource/qx/tool/website/src/assets/bluebird.min.js +0 -4615
@@ -0,0 +1,738 @@
1
+ /* ************************************************************************
2
+
3
+ qooxdoo - the new era of web development
4
+
5
+ http://qooxdoo.org
6
+
7
+ Copyright:
8
+ 2016 Zenesis Limited, http://www.zenesis.com
9
+
10
+ License:
11
+ MIT: https://opensource.org/licenses/MIT
12
+ See the LICENSE file in the project's top-level directory for details.
13
+
14
+ Authors:
15
+ * John Spackman (john.spackman@zenesis.com)
16
+ * Patryk Malinowski (pmalinowski@vmn.digital)
17
+
18
+ ************************************************************************ */
19
+
20
+ /**
21
+ * Wrapper around a native promise, adding some extra helpful methods which are found in Bluebird.js,
22
+ * such as .map, .reduce, .filter, and many more.
23
+ *
24
+ * @ignore(AggregateError)
25
+ */
26
+ qx.Class.define("qx.promise.NativeWrapper", {
27
+ extend: qx.core.Object,
28
+
29
+ /**
30
+ * @overload
31
+ * @param {(resolve: Function, reject: Function) => void} arg0 The executor for the promise
32
+ *
33
+ * @overload
34
+ * Wraps a native promise in the wrapper class
35
+ * @param {Promise} arg0 A native Promise
36
+ */
37
+ construct(arg0) {
38
+ super();
39
+ if (typeof arg0 === "function") {
40
+ this.__promise = new Promise(arg0);
41
+ } else if (typeof arg0 === "object" && arg0.constructor === Promise) {
42
+ this.__promise = arg0;
43
+ }
44
+ },
45
+
46
+ members: {
47
+ /**
48
+ * @type {Object} The context that this promise is bound to
49
+ */
50
+ __context: null,
51
+
52
+ /**
53
+ * Creates a new promise just like this one, but with a context set
54
+ * @see
55
+ * @param {Object} context
56
+ * @returns
57
+ */
58
+ bind(context) {
59
+ let promise = new qx.promise.NativeWrapper(this.__promise);
60
+ return promise.__setContext(context);
61
+ },
62
+
63
+ /**
64
+ * Same as for Native Promise
65
+ * @returns {qx.promise.NativeWrapper}
66
+ */
67
+ then(onResolved, onRejected) {
68
+ onResolved = onResolved.bind(this.__context);
69
+ if (onRejected) {
70
+ onRejected = onRejected.bind(this.__context);
71
+ }
72
+ return qx.promise.NativeWrapper.__wrap(
73
+ this.__promise.then(onResolved, onRejected)
74
+ ).__setContext(this.__context);
75
+ },
76
+
77
+ /**
78
+ * Same as for Native Promise
79
+ * @returns {qx.promise.NativeWrapper}
80
+ */
81
+ catch(handler) {
82
+ handler = handler.bind(this.__context);
83
+ return qx.promise.NativeWrapper.__wrap(
84
+ this.__promise.catch(handler)
85
+ ).__setContext(this.__context);
86
+ },
87
+
88
+ /**
89
+ * Same as for Native Promise
90
+ * @returns {qx.promise.NativeWrapper}
91
+ */
92
+ spread(fulfilledHandler) {
93
+ return this.then(values => fulfilledHandler(...values));
94
+ },
95
+
96
+ /**
97
+ * Same as for Native Promise
98
+ * @returns {qx.promise.NativeWrapper}
99
+ */
100
+ finally(handler) {
101
+ handler = handler.bind(this.__context);
102
+ return qx.promise.NativeWrapper.__wrap(
103
+ this.__promise.finally(handler)
104
+ ).__setContext(this.__context);
105
+ },
106
+
107
+ /**
108
+ * Due to the high complexity of implementing this feature, it is not supported in qx.promise.NativeWrapper
109
+ */
110
+ cancel() {
111
+ throw new Error(
112
+ "qx.promise.NativeWrapper does not support canceling promises"
113
+ );
114
+ },
115
+
116
+ /**
117
+ * Note: Only call when this promise will resolve to an array
118
+ * Same as Promise.all, but passed with the array that this promise resolves to
119
+ * @returns {qx.promise.NativeWrapper}
120
+ */
121
+ all(...args) {
122
+ return qx.promise.NativeWrapper.all(this, ...args);
123
+ },
124
+
125
+ /**
126
+ * Note: Only call when this promise will resolve to an array
127
+ * Same as Promise.race, but passed with the array that this promise resolves to
128
+ * @returns {qx.promise.NativeWrapper}
129
+ */
130
+ race() {
131
+ return qx.promise.NativeWrapper.race(this);
132
+ },
133
+
134
+ /**
135
+ * Note: Only call when this promise will resolve to an array
136
+ * Same as Promise.any, but passed with the array that this promise resolves to
137
+ * @returns {qx.promise.NativeWrapper}
138
+ */
139
+ any() {
140
+ return qx.promise.NativeWrapper.any(this);
141
+ },
142
+
143
+ /**
144
+ * Same as {@link qx.promise.NativeWrapper.some} except that it iterates over the value of this promise, when
145
+ * it is fulfilled; return a promise that is fulfilled as soon as count promises are fulfilled
146
+ * in the array. The fulfillment value is an array with count values in the order they were fulfilled.
147
+ *
148
+ * @param count {Integer}
149
+ * @return {qx.promise.NativeWrapper}
150
+ */
151
+ some(count) {
152
+ return qx.promise.NativeWrapper.some(this, count);
153
+ },
154
+
155
+ /**
156
+ * Same as {@link qx.promise.NativeWrapper.each} except that it iterates over the value of this promise, when
157
+ * it is fulfilled; iterates over the values with the given <code>iterator</code> function with the signature
158
+ * <code>(value, index, length)</code> where <code>value</code> is the resolved value. Iteration happens
159
+ * serially. If any promise is rejected the returned promise is rejected as well.
160
+ *
161
+ * Resolves to the original array unmodified, this method is meant to be used for side effects. If the iterator
162
+ * function returns a promise or a thenable, then the result of the promise is awaited, before continuing with
163
+ * next iteration.
164
+ *
165
+ * @param iterator {Function} the callback, with <code>(value, index, length)</code>
166
+ * @return {qx.promise.NativeWrapper}
167
+ */
168
+ each(iterator) {
169
+ return qx.promise.NativeWrapper.each(this, iterator);
170
+ },
171
+
172
+ /**
173
+ * Same as {@link qx.promise.NativeWrapper.filter} except that it iterates over the value of this promise, when it is fulfilled;
174
+ * iterates over all the values into an array and filter the array to another using the given filterer function.
175
+ *
176
+ * @param iterable {Iterable} An iterable object, such as an Array
177
+ * @param iterator {Function} the callback, with <code>(value, index, length)</code>
178
+ * @param options {Object?} options; can be:
179
+ * <code>concurrency</code> max nuber of simultaneous filters, default is <code>Infinity</code>
180
+ * @return {qx.promise.NativeWrapper}
181
+ */
182
+ filter(iterator, options) {
183
+ return qx.promise.NativeWrapper.filter(this, iterator, options);
184
+ },
185
+
186
+ /**
187
+ * Same as {@link qx.promise.NativeWrapper.map} except that it iterates over the value of this promise, when it is fulfilled;
188
+ * iterates over all the values into an array and map the array to another using the given mapper function.
189
+ *
190
+ * Promises returned by the mapper function are awaited for and the returned promise doesn't fulfill
191
+ * until all mapped promises have fulfilled as well. If any promise in the array is rejected, or
192
+ * any promise returned by the mapper function is rejected, the returned promise is rejected as well.
193
+ *
194
+ * The mapper function for a given item is called as soon as possible, that is, when the promise
195
+ * for that item's index in the input array is fulfilled. This doesn't mean that the result array
196
+ * has items in random order, it means that .map can be used for concurrency coordination unlike
197
+ * .all.
198
+ *
199
+ * @param iterator {Function} the callback, with <code>(value, index, length)</code>
200
+ * @param options {Object?} * A native object with one key: <code>concurrency</code>: max number of simultaneous maps, default is <code>Infinity</code>
201
+ * @return {qx.promise.NativeWrapper}
202
+ */
203
+ map(iterator, options) {
204
+ return qx.promise.NativeWrapper.map(this, iterator, options);
205
+ },
206
+
207
+ /**
208
+ * Same as {@link qx.promise.NativeWrapper.mapSeries} except that it iterates over the value of this promise, when
209
+ * it is fulfilled; iterates over all the values into an array and iterate over the array serially,
210
+ * in-order.
211
+ *
212
+ * Returns a promise for an array that contains the values returned by the iterator function in their
213
+ * respective positions. The iterator won't be called for an item until its previous item, and the
214
+ * promise returned by the iterator for that item are fulfilled. This results in a mapSeries kind of
215
+ * utility but it can also be used simply as a side effect iterator similar to Array#forEach.
216
+ *
217
+ * If any promise in the input array is rejected or any promise returned by the iterator function is
218
+ * rejected, the result will be rejected as well.
219
+ *
220
+ * @param iterator {Function} the callback, with <code>(value, index, length)</code>
221
+ * @return {qx.promise.NativeWrapper}
222
+ */
223
+ mapSeries(iterator, options) {
224
+ return qx.promise.NativeWrapper.mapSeries(this, iterator, options);
225
+ },
226
+
227
+ /**
228
+ * Same as {@link qx.promise.NativeWrapper.reduce} except that it iterates over the value of this promise, when
229
+ * it is fulfilled; iterates over all the values in the <code>Iterable</code> into an array and
230
+ * reduce the array to a value using the given reducer function.
231
+ *
232
+ * If the reducer function returns a promise, then the result of the promise is awaited, before
233
+ * continuing with next iteration. If any promise in the array is rejected or a promise returned
234
+ * by the reducer function is rejected, the result is rejected as well.
235
+ *
236
+ * If initialValue is undefined (or a promise that resolves to undefined) and the iterable contains
237
+ * only 1 item, the callback will not be called and the iterable's single item is returned. If the
238
+ * iterable is empty, the callback will not be called and initialValue is returned (which may be
239
+ * undefined).
240
+ *
241
+ * qx.promise.NativeWrapper.reduce will start calling the reducer as soon as possible, this is why you might want to
242
+ * use it over qx.promise.NativeWrapper.all (which awaits for the entire array before you can call Array#reduce on it).
243
+ *
244
+ * @param reducer {Function} the callback, with <code>(value, index, length)</code>
245
+ * @param initialValue {Object?} optional initial value
246
+ * @return {qx.promise.NativeWrapper}
247
+ */
248
+ reduce(reducer, initialValue) {
249
+ return qx.promise.NativeWrapper.reduce(this, reducer, initialValue);
250
+ },
251
+
252
+ /**
253
+ *
254
+ * @param {Object} context
255
+ * @returns {qx.promise.NativeWrapper} this object to support chaining
256
+ */
257
+ __setContext(context) {
258
+ this.__context = context;
259
+ return this;
260
+ }
261
+ },
262
+
263
+ statics: {
264
+ /**
265
+ * Wraps a promise in a qx.promise.NativeWrapper
266
+ * @param {Promise} promise
267
+ * @returns
268
+ */
269
+ __wrap(promise) {
270
+ if (qx.core.Environment.get("qx.debug")) {
271
+ if (promise.constructor !== Promise) {
272
+ throw new Error("Only native promises can be wrapped!");
273
+ }
274
+ }
275
+
276
+ return new qx.promise.NativeWrapper(promise);
277
+ },
278
+ /**
279
+ * Returns a Promise object that is resolved with the given value. If the value is a thenable (i.e.
280
+ * has a then method), the returned promise will "follow" that thenable, adopting its eventual
281
+ * state; otherwise the returned promise will be fulfilled with the value. Generally, if you
282
+ * don't know if a value is a promise or not, Promise.resolve(value) it instead and work with
283
+ * the return value as a promise.
284
+ *
285
+ * @param value {Object}
286
+ * @return {qx.promise.NativeWrapper}
287
+ */
288
+ resolve(value) {
289
+ return qx.promise.NativeWrapper.__wrap(Promise.resolve(value));
290
+ },
291
+
292
+ /**
293
+ * Returns a Promise object that is rejected with the given reason.
294
+ * @param reason {Object?} Reason why this Promise rejected. A warning is generated if not instanceof Error. If undefined, a default Error is used.
295
+ * @return {qx.promise.NativeWrapper}
296
+ */
297
+ reject(reason) {
298
+ return qx.promise.NativeWrapper.__wrap(Promise.reject(reason));
299
+ },
300
+
301
+ /**
302
+ * Returns a promise that resolves when all of the promises in the object properties have resolved,
303
+ * or rejects with the reason of the first passed promise that rejects. The result of each property
304
+ * is placed back in the object, replacing the promise. Note that non-promise values are untouched.
305
+ *
306
+ * @param value {var} An object
307
+ * @return {qx.promise.NativeWrapper}
308
+ */
309
+ allOf(value) {
310
+ function action(value) {
311
+ var arr = [];
312
+ var names = [];
313
+ for (var name in value) {
314
+ if (value.hasOwnProperty(name) && qx.Promise.isPromise(value[name])) {
315
+ arr.push(value[name]);
316
+ names.push(name);
317
+ }
318
+ }
319
+ return qx.promise.NativeWrapper.all(arr).then(function (arr) {
320
+ arr.forEach(function (item, index) {
321
+ value[names[index]] = item;
322
+ });
323
+ return value;
324
+ });
325
+ }
326
+ return qx.Promise.isPromise(value) ? value.then(action) : action(value);
327
+ },
328
+
329
+ /**
330
+ * Returns a promise that resolves when all of the promises in the iterable argument have resolved,
331
+ * or rejects with the reason of the first passed promise that rejects. Note that non-promise values
332
+ * are untouched.
333
+ *
334
+ * @param iterable {Iterable} An iterable object, such as an Array
335
+ * @return {qx.promise.NativeWrapper}
336
+ */
337
+ all(iterable) {
338
+ return qx.promise.NativeWrapper.resolve(iterable).then(iterable =>
339
+ qx.promise.NativeWrapper.__wrap(Promise.all(iterable))
340
+ );
341
+ },
342
+
343
+ /**
344
+ * Returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves
345
+ * or rejects, with the value or reason from that promise.
346
+ * @param iterable {Iterable} An iterable object, such as an Array
347
+ * @return {qx.promise.NativeWrapper}
348
+ */
349
+ race(iterable) {
350
+ return qx.promise.NativeWrapper.resolve(iterable).then(
351
+ iterableResolved =>
352
+ new qx.promise.NativeWrapper(Promise.race(iterableResolved))
353
+ );
354
+ },
355
+
356
+ /* *********************************************************************************
357
+ *
358
+ * Extension API methods
359
+ *
360
+ */
361
+
362
+ /**
363
+ * Like Promise.some, with 1 as count. However, if the promise fulfills, the fulfillment value is not an
364
+ * array of 1 but the value directly.
365
+ *
366
+ * @param iterable {Iterable} An iterable object, such as an Array
367
+ * @return {qx.promise.NativeWrapper}
368
+ */
369
+ any(iterable) {
370
+ return qx.promise.NativeWrapper.resolve(iterable).then(
371
+ iterableResolved =>
372
+ new qx.promise.NativeWrapper(Promise.any(iterableResolved))
373
+ );
374
+ },
375
+
376
+ /**
377
+ * Given an Iterable (arrays are Iterable), or a promise of an Iterable, which produces promises (or a mix
378
+ * of promises and values), iterate over all the values in the Iterable into an array and return a promise
379
+ * that is fulfilled as soon as count promises are fulfilled in the array. The fulfillment value is an
380
+ * array with count values in the order they were fulfilled.
381
+ *
382
+ * @param iterable {Iterable} An iterable object, such as an Array
383
+ * @param count {Integer}
384
+ * @return {qx.promise.NativeWrapper}
385
+ */
386
+ some(iterable, count) {
387
+ return new qx.promise.NativeWrapper((resolve, reject) => {
388
+ qx.promise.NativeWrapper.resolve(iterable).then(iterable => {
389
+ let resolved = [];
390
+ let rejected = [];
391
+ let minToReject = iterable.length - count + 1;
392
+
393
+ const onResolved = value => {
394
+ if (resolved.length >= count) {
395
+ return;
396
+ }
397
+ resolved.push(value);
398
+ if (resolved.length == count) {
399
+ resolve(resolved);
400
+ }
401
+ };
402
+
403
+ const onRejected = reason => {
404
+ rejected.push(reason);
405
+ if (--minToReject == 0) {
406
+ reject(new AggregateError(rejected));
407
+ }
408
+ };
409
+ iterable.forEach((elem, index) => {
410
+ if (qx.Promise.isPromise(elem)) {
411
+ elem.then(onResolved, onRejected);
412
+ } else {
413
+ onResolved(elem);
414
+ }
415
+ });
416
+ });
417
+ });
418
+ },
419
+
420
+ /**
421
+ * Iterate over an array, or a promise of an array, which contains promises (or a mix of promises and values)
422
+ * with the given <code>iterator</code> function with the signature <code>(value, index, length)</code> where
423
+ * <code>value</code> is the resolved value of a respective promise in the input array. Iteration happens
424
+ * serially. If any promise in the input array is rejected the returned promise is rejected as well.
425
+ *
426
+ * Resolves to the original array unmodified, this method is meant to be used for side effects. If the iterator
427
+ * function returns a promise or a thenable, then the result of the promise is awaited, before continuing with
428
+ * next iteration.
429
+ *
430
+ * @param iterable {Iterable} An iterable object, such as an Array
431
+ * @param iterator {Function} the callback, with <code>(value, index, length)</code>
432
+ * @return {qx.promise.NativeWrapper}
433
+ */
434
+ each(iterable, iterator) {
435
+ let f = async () => {
436
+ let iterableValue = await iterable;
437
+ let index = 0;
438
+
439
+ for (let item of iterableValue) {
440
+ let itemResolved = await item;
441
+ await iterator(itemResolved, index++, iterable.length);
442
+ }
443
+ };
444
+ return new qx.promise.NativeWrapper(f());
445
+ },
446
+
447
+ /**
448
+ * Given an Iterable(arrays are Iterable), or a promise of an Iterable, which produces promises (or a mix of
449
+ * promises and values), iterate over all the values in the Iterable into an array and filter the array to
450
+ * another using the given filterer function.
451
+ *
452
+ * It is essentially an efficient shortcut for doing a .map and then Array#filter:
453
+ * <pre>
454
+ * qx.promise.NativeWrapper.map(valuesToBeFiltered, function(value, index, length) {
455
+ * return Promise.all([filterer(value, index, length), value]);
456
+ * }).then(function(values) {
457
+ * return values.filter(function(stuff) {
458
+ * return stuff[0] == true
459
+ * }).map(function(stuff) {
460
+ * return stuff[1];
461
+ * });
462
+ * });
463
+ * </pre>
464
+ *
465
+ * @param iterable {Iterable} An iterable object, such as an Array
466
+ * @param iterator {Function} the callback, with <code>(value, index, length)</code>
467
+ * @param options {Object?} Either:
468
+ * A native object with one key: <code>concurrency</code>: max number of simultaneous filters, default is <code>Infinity</code>
469
+ * Or: any other object, in which case this will be the context for the iterator
470
+ * @return {qx.promise.NativeWrapper}
471
+ */
472
+ filter(iterable, iterator, options) {
473
+ let limiter = new qx.util.ConcurrencyLimiter(options?.concurrency);
474
+
475
+ const doit = async () => {
476
+ let iterableResolved = await iterable;
477
+ let resultsPromises = iterableResolved.map((item, index) =>
478
+ limiter.add(async () => {
479
+ let itemResolved = await item;
480
+ let keep = await iterator(
481
+ itemResolved,
482
+ index,
483
+ iterableResolved.length
484
+ );
485
+ return { keep, val: itemResolved };
486
+ })
487
+ );
488
+
489
+ let values = await qx.promise.NativeWrapper.all(resultsPromises);
490
+ return values.filter(({ keep }) => keep).map(({ val }) => val);
491
+ };
492
+
493
+ return new qx.promise.NativeWrapper(doit());
494
+ },
495
+
496
+ /**
497
+ * Given an <code>Iterable</code> (arrays are <code>Iterable</code>), or a promise of an
498
+ * <code>Iterable</code>, which produces promises (or a mix of promises and values), iterate over
499
+ * all the values in the <code>Iterable</code> into an array and map the array to another using
500
+ * the given mapper function.
501
+ *
502
+ * Promises returned by the mapper function are awaited for and the returned promise doesn't fulfill
503
+ * until all mapped promises have fulfilled as well. If any promise in the array is rejected, or
504
+ * any promise returned by the mapper function is rejected, the returned promise is rejected as well.
505
+ *
506
+ * The mapper function for a given item is called as soon as possible, that is, when the promise
507
+ * for that item's index in the input array is fulfilled. This doesn't mean that the result array
508
+ * has items in random order, it means that .map can be used for concurrency coordination unlike
509
+ * .all.
510
+ *
511
+ * A common use of Promise.map is to replace the .push+Promise.all boilerplate:
512
+ *
513
+ * <pre>
514
+ * var promises = [];
515
+ * for (var i = 0; i < fileNames.length; ++i) {
516
+ * promises.push(fs.readFileAsync(fileNames[i]));
517
+ * }
518
+ * qx.promise.NativeWrapper.all(promises).then(function() {
519
+ * console.log("done");
520
+ * });
521
+ *
522
+ * // Using Promise.map:
523
+ * qx.promise.NativeWrapper.map(fileNames, function(fileName) {
524
+ * // Promise.map awaits for returned promises as well.
525
+ * return fs.readFileAsync(fileName);
526
+ * }).then(function() {
527
+ * console.log("done");
528
+ * });
529
+ * </pre>
530
+ *
531
+ * @param iterable {Iterable} An iterable object, such as an Array
532
+ * @param iterator {Function} the callback, with <code>(value, index, length)</code>
533
+ * @param options {Object?} * A native object with one key: <code>concurrency</code>: max number of simultaneous maps, default is <code>Infinity</code>
534
+ * @return {qx.promise.NativeWrapper}
535
+ */
536
+ map(iterable, iterator, options) {
537
+ return qx.promise.NativeWrapper.resolve(iterable).then(iterable => {
538
+ let limiter = new qx.util.ConcurrencyLimiter(options?.concurrency);
539
+
540
+ let resultsPromises = iterable.map((item, index) =>
541
+ limiter.add(async () => {
542
+ let itemResolved = await item;
543
+ let result = await iterator(itemResolved, index, iterable.length);
544
+ return result;
545
+ })
546
+ );
547
+
548
+ return qx.promise.NativeWrapper.all(resultsPromises);
549
+ });
550
+ },
551
+
552
+ /**
553
+ * Given an <code>Iterable</code>(arrays are <code>Iterable</code>), or a promise of an
554
+ * <code>Iterable</code>, which produces promises (or a mix of promises and values), iterate over
555
+ * all the values in the <code>Iterable</code> into an array and iterate over the array serially,
556
+ * in-order.
557
+ *
558
+ * Returns a promise for an array that contains the values returned by the iterator function in their
559
+ * respective positions. The iterator won't be called for an item until its previous item, and the
560
+ * promise returned by the iterator for that item are fulfilled. This results in a mapSeries kind of
561
+ * utility but it can also be used simply as a side effect iterator similar to Array#forEach.
562
+ *
563
+ * If any promise in the input array is rejected or any promise returned by the iterator function is
564
+ * rejected, the result will be rejected as well.
565
+ *
566
+ * Example where .mapSeries(the instance method) is used for iterating with side effects:
567
+ *
568
+ * <pre>
569
+ * // Source: http://jakearchibald.com/2014/es7-async-functions/
570
+ * function loadStory() {
571
+ * return getJSON('story.json')
572
+ * .then(function(story) {
573
+ * addHtmlToPage(story.heading);
574
+ * return story.chapterURLs.map(getJSON);
575
+ * })
576
+ * .mapSeries(function(chapter) { addHtmlToPage(chapter.html); })
577
+ * .then(function() { addTextToPage("All done"); })
578
+ * .catch(function(err) { addTextToPage("Argh, broken: " + err.message); })
579
+ * .then(function() { document.querySelector('.spinner').style.display = 'none'; });
580
+ * }
581
+ * </pre>
582
+ *
583
+ * @param iterable {Iterable} An iterable object, such as an Array
584
+ * @param iterator {Function} the callback, with <code>(value, index, length)</code>
585
+ * @return {qx.promise.NativeWrapper}
586
+ */
587
+ mapSeries(iterable, iterator) {
588
+ return new qx.promise.NativeWrapper(async (resolve, reject) => {
589
+ let failed = false;
590
+ const fail = reason => {
591
+ if (!failed) {
592
+ failed = true;
593
+ reject(reason);
594
+ }
595
+ };
596
+
597
+ //We must handle the rejections of promises ASAP
598
+ //to prevent unhandled promise rejections
599
+ qx.promise.NativeWrapper.all(iterable).catch(fail);
600
+
601
+ let result = [];
602
+
603
+ iterable = await iterable;
604
+
605
+ try {
606
+ let index = 0;
607
+ for (let promise of iterable) {
608
+ let value = await promise;
609
+ let mapped = await iterator(value, index++, iterable.length);
610
+ result.push(mapped);
611
+ }
612
+ } catch (ex) {
613
+ fail(ex);
614
+ }
615
+
616
+ resolve(result);
617
+ });
618
+ },
619
+
620
+ /**
621
+ * Given an <code>Iterable</code> (arrays are <code>Iterable</code>), or a promise of an
622
+ * <code>Iterable</code>, which produces promises (or a mix of promises and values), iterate
623
+ * over all the values in the <code>Iterable</code> into an array and reduce the array to a
624
+ * value using the given reducer function.
625
+ *
626
+ * If the reducer function returns a promise, then the result of the promise is awaited, before
627
+ * continuing with next iteration. If any promise in the array is rejected or a promise returned
628
+ * by the reducer function is rejected, the result is rejected as well.
629
+ *
630
+ * Read given files sequentially while summing their contents as an integer. Each file contains
631
+ * just the text 10.
632
+ *
633
+ * <pre>
634
+ * qx.promise.NativeWrapper.reduce(["file1.txt", "file2.txt", "file3.txt"], function(total, fileName) {
635
+ * return fs.readFileAsync(fileName, "utf8").then(function(contents) {
636
+ * return total + parseInt(contents, 10);
637
+ * });
638
+ * }, 0).then(function(total) {
639
+ * //Total is 30
640
+ * });
641
+ * </pre>
642
+ *
643
+ * If initialValue is undefined (or a promise that resolves to undefined) and the iterable contains
644
+ * only 1 item, the callback will not be called and the iterable's single item is returned. If the
645
+ * iterable is empty, the callback will not be called and initialValue is returned (which may be
646
+ * undefined).
647
+ *
648
+ * Promise.reduce will start calling the reducer as soon as possible, this is why you might want to
649
+ * use it over Promise.all (which awaits for the entire array before you can call Array#reduce on it).
650
+ *
651
+ * @param iterable {Iterable} An iterable object, such as an Array
652
+ * @param reducer {Function} the callback, with <code>(value, index, length)</code>
653
+ * @param initialValue {Object?} optional initial value
654
+ * @return {qx.promise.NativeWrapper}
655
+ */
656
+ reduce(iterable, reducer, initialValue) {
657
+ return new qx.promise.NativeWrapper(async (resolve, reject) => {
658
+ let failed = false;
659
+ function fail(reason) {
660
+ if (!failed) {
661
+ failed = true;
662
+ reject(reason);
663
+ }
664
+ }
665
+
666
+ try {
667
+ let iterableResolved = await iterable;
668
+
669
+ //We must handle the rejections of promises ASAP
670
+ //to prevent unhandled promise rejections
671
+ iterableResolved.forEach((item, index) => {
672
+ if (qx.Promise.isPromise(item)) {
673
+ item.catch(fail);
674
+ }
675
+ });
676
+
677
+ let accum = initialValue;
678
+ let index = 0;
679
+ for (let promise of iterableResolved) {
680
+ let data = await promise;
681
+ accum = await reducer(accum, data, index, iterableResolved.length);
682
+ index++;
683
+ }
684
+ resolve(accum);
685
+ } catch (ex) {
686
+ fail(ex);
687
+ }
688
+ });
689
+ },
690
+
691
+ /**
692
+ * Returns a new function that wraps the given function fn. The new function will always return a promise that is
693
+ * fulfilled with the original functions return values or rejected with thrown exceptions from the original function.
694
+ * @param cb {Function}
695
+ * @return {Function}
696
+ */
697
+ method(cb) {
698
+ return (...args) =>
699
+ new qx.promise.NativeWrapper(resolve =>
700
+ resolve(cb.call(this.__context, ...args))
701
+ );
702
+ },
703
+
704
+ /**
705
+ * Like .all but for object properties or Maps* entries instead of iterated values. Returns a promise that
706
+ * is fulfilled when all the properties of the object or the Map's' values** are fulfilled. The promise's
707
+ * fulfillment value is an object or a Map with fulfillment values at respective keys to the original object
708
+ * or a Map. If any promise in the object or Map rejects, the returned promise is rejected with the rejection
709
+ * reason.
710
+ *
711
+ * If object is a trusted Promise, then it will be treated as a promise for object rather than for its
712
+ * properties. All other objects (except Maps) are treated for their properties as is returned by
713
+ * Object.keys - the object's own enumerable properties.
714
+ *
715
+ * @param input {Object} An Object
716
+ * @return {qx.promise.NativeWrapper}
717
+ */
718
+ props(input) {
719
+ return qx.promise.NativeWrapper.resolve(input).then(input => {
720
+ let entries = Object.entries(input);
721
+ let promises = entries.map(
722
+ entry =>
723
+ new qx.promise.NativeWrapper(async resolve => {
724
+ const value = await entry[1];
725
+ resolve([entry[0], value]);
726
+ })
727
+ );
728
+ return qx.promise.NativeWrapper.all(promises).then(values => {
729
+ let result = {};
730
+ values.forEach(entry => {
731
+ result[entry[0]] = entry[1];
732
+ });
733
+ return result;
734
+ });
735
+ });
736
+ }
737
+ }
738
+ });