UNPKG

62.9 kBJavaScriptView Raw
1/*!
2 * async-af/esm v7.0.37
3 *
4 * AsyncAF (The asynciest of async libs there ever was or ever will be...AsyncAF!?)
5 * (https://async-af.js.org/AsyncAF)
6 *
7 * Copyright (c) 2017-present, Scott Rudiger (https://github.com/ScottRudiger)
8 *
9 * This source code is licensed under the MIT license found in this library's
10 * GitHub repository (https://github.com/AsyncAF/AsyncAF/blob/master/LICENSE).
11 */
12function _defineProperty(obj, key, value) {
13 if (key in obj) {
14 Object.defineProperty(obj, key, {
15 value: value,
16 enumerable: true,
17 configurable: true,
18 writable: true
19 });
20 } else {
21 obj[key] = value;
22 }
23
24 return obj;
25}
26
27function _objectSpread(target) {
28 for (var i = 1; i < arguments.length; i++) {
29 var source = arguments[i] != null ? arguments[i] : {};
30 var ownKeys = Object.keys(source);
31
32 if (typeof Object.getOwnPropertySymbols === 'function') {
33 ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
34 return Object.getOwnPropertyDescriptor(source, sym).enumerable;
35 }));
36 }
37
38 ownKeys.forEach(function (key) {
39 _defineProperty(target, key, source[key]);
40 });
41 }
42
43 return target;
44}
45
46function _taggedTemplateLiteral(strings, raw) {
47 if (!raw) {
48 raw = strings.slice(0);
49 }
50
51 return Object.freeze(Object.defineProperties(strings, {
52 raw: {
53 value: Object.freeze(raw)
54 }
55 }));
56}
57
58const nameFunction = function nameFunction(fn, name) {
59 return Object.defineProperty(fn, 'name', {
60 value: name,
61 configurable: true
62 });
63};
64
65const createNewlessClass = Class => {
66 const {
67 name
68 } = Class;
69
70 const Newless = function Newless() {
71 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
72 args[_key] = arguments[_key];
73 }
74
75 return new Class(...args);
76 };
77
78 Newless.prototype = Class.prototype;
79 Object.setPrototypeOf(Newless, Class);
80 Newless.prototype.constructor = Newless;
81 return nameFunction(Newless, name);
82};
83
84/**
85 * adds prototype/static methods to AsyncAF or AsyncAfWrapper
86 *
87 * see {@link AsyncAfWrapper AsyncAfWrapper} for an example of how to cherry-pick AsyncAF methods you'd like to use rather than pulling in the entire AsyncAF library;
88 *
89 * for something different, the following shows how to add custom methods to AsyncAF & AsyncAfWrapper
90 *
91 * **Example**
92 *
93 * say you want to extend AsyncAF with your own prototype method that acts on an array of numbers or promises that resolve to numbers and naively adds them up
94 *
95 * let's call it sumAF; here's some code:
96 *
97 * ```js
98 * // sumAF.js
99 *
100 * const sumAF = function () {
101 * return this.then(nums => Promise.all(nums))
102 * .then(nums => nums.reduce((sum, num) => sum + num));
103 * };
104 *
105 * export default sumAF;
106 * ```
107 *
108 * pull in {@link AsyncAF AsyncAF} or {@link AsyncAfWrapper AsyncAfWrapper} and `sumAF` to the file you'd like to use it in:
109 *
110 * ```js
111 * // otherFile.js
112 *
113 * import AsyncAF from 'async-af'; // or import AsyncAF from '@async-af/wrapper';
114 * import sumAF from './sumAF';
115 * ```
116 *
117 * then, call `use` on `AsyncAF` and pass in `sumAF` wrapped in an object to the first parameter, `prototypeMethods`:
118 *
119 * ```js
120 * // otherFile.js
121 * // ...
122 *
123 * AsyncAF.use({sumAF});
124 * ```
125 *
126 * ready! now your custom prototype method will be available on AsyncAF
127 *
128 * ```js
129 * // otherFile.js
130 * // ...
131 *
132 * const promises = [1, 2, 3].map(n => Promise.resolve(n));
133 *
134 * const sum = AsyncAF(promises).sumAF()
135 *
136 * AsyncAF.logAF(sum);
137 * // @otherFile.js:10:9:
138 * // 6
139 * // in 0.001 secs
140 * ```
141 *
142 * if you'd like to add a static method to AsyncAF, `use` accepts a second optional argument `staticMethods`; for example:
143 *
144 * ```js
145 * const staticNoop = () => {};
146 *
147 * AsyncAF.use({}, {staticNoop});
148 *
149 * AsyncAF.staticNoop(); // noop
150 * ```
151 *
152 * @static
153 * @param {Object} prototypeMethods an Object containing the prototype methods you'd like to use
154 * @param {Object=} staticMethods an Object containing the static methods you'd like to use
155 * @returns {undefined} adds prototype/static methods to AsyncAF or AsyncAfWrapper
156 * @since 3.0.0
157 * @see AsyncAF
158 * @see AsyncAfWrapper
159 * @see {@tutorial TOO_MANY_IMPORTS}
160 * @memberof AsyncAfWrapper
161 * @alias AsyncAfWrapper#use
162 */
163const use = function use(prototypeMethods) {
164 let staticMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
165 if (typeof prototypeMethods !== 'object') throw TypeError('prototypeMethods param accepts an Object containing the prototypeMethods you\'d like to add to the AsyncAF prototype, or an empty Object');
166 if (typeof staticMethods !== 'object') throw TypeError('staticMethods param accepts an Object containing the staticMethods you\'d like to add to AsyncAF');
167 Object.assign(this.prototype, prototypeMethods);
168 Object.assign(this, staticMethods);
169};
170
171const inSeries = new WeakMap();
172const series = {
173 inSeries: {
174 get() {
175 return inSeries.get(this);
176 }
177
178 },
179
180 /**
181 * indicates that the next method invoked should be performed in series
182 *
183 * when you need to perform a method in series rather than in parallel, prepend the method with `series`; e.g.:
184 * ```js
185 * AsyncAF(promises).series.forEachAF(callback)
186 * ```
187 *
188 * `series` can currently be chained with:
189 * - {@link AsyncAF#everyAF everyAF}
190 * - {@link AsyncAF#filterAF filterAF}
191 * - {@link AsyncAF#findAF findAF}
192 * - {@link AsyncAF#findIndexAF findIndexAF}
193 * - {@link AsyncAF#forEachAF forEachAF}
194 * - {@link AsyncAF#includesAF includesAF}
195 * - {@link AsyncAF#indexOfAF indexOfAF}
196 * - {@link AsyncAF#lastIndexOfAF lastIndexOfAF}
197 * - {@link AsyncAF#mapAF mapAF}
198 * - {@link AsyncAF#reduceAF reduceAF}
199 * - {@link AsyncAF#someAF someAF}
200 *
201 * @example
202 * import delay from 'delay'; // {@link https://www.npmjs.com/package/delay}
203 *
204 * const nums = [2, 1];
205 *
206 * // perform a serial forEach by chaining {@link AsyncAF#series series} and {@link AsyncAF#forEachAF forEachAF}
207 * (async () => {
208 * const start = Date.now();
209 *
210 * await AsyncAF(nums).series.forEachAF(async num => {
211 * await delay(num * 1000);
212 * console.log(num, `at ~${Date.now() - start} ms`);
213 * });
214 *
215 * console.log(`total: ~${Date.now() - start} ms`);
216 * })();
217 *
218 * // logs:
219 * // 2 'at ~2000 ms'
220 * // 1 'at ~3000 ms'
221 * // total: ~3000 ms
222 *
223 *
224 * // perform a parallel forEach by omitting {@link AsyncAF#series series}
225 * (async () => {
226 * const start = Date.now();
227 *
228 * await AsyncAF(nums).forEachAF(async num => {
229 * await delay(num * 1000);
230 * console.log(num, `at ~${Date.now() - start} ms`);
231 * });
232 *
233 * console.log(`total: ~${Date.now() - start} ms`);
234 * })();
235 *
236 * // logs:
237 * // 1 'at ~1000 ms'
238 * // 2 'at ~2000 ms'
239 * // total: ~2000 ms
240 *
241 * @function series
242 * @returns {AsyncAF.<any>} returns an instance of AsyncAF that will perform the next method invocation serially
243 * @since 7.0.0
244 * @see {@link AsyncAF#io io} (alias)
245 * @memberof AsyncAF#
246 */
247 series: {
248 get() {
249 inSeries.set(this, !this.inSeries);
250 return this;
251 }
252
253 },
254
255 /**
256 * `io` (in order) indicates that the next method invoked should be performed in series
257 *
258 * when you need to perform a method in series rather than in parallel, prepend the method with `io`; e.g.:
259 * ```js
260 * AsyncAF(promises).io.forEachAF(callback)
261 * ```
262 *
263 * `io` is an alias for `series`; see {@link AsyncAF#series series's documentation} for more
264 * @function io
265 * @returns {AsyncAF.<any>} returns an instance of AsyncAF that will perform the next method invocation serially
266 * @since 7.0.0
267 * @see {@link AsyncAF#series series} (alias)
268 * @memberof AsyncAF#
269 */
270 io: {
271 get() {
272 return this.series;
273 }
274
275 }
276};
277
278const dataStore = new WeakMap();
279
280class AsyncAfWrapperProto {
281 constructor(data) {
282 dataStore.set(this, Promise.resolve(data));
283 }
284
285 then(resolve, reject) {
286 return this.constructor(dataStore.get(this).then(resolve, reject));
287 }
288
289 catch(reject) {
290 return this.then(null, reject);
291 }
292
293 finally(onFinally) {
294 return dataStore.get(this).finally(onFinally);
295 }
296
297}
298
299AsyncAfWrapperProto.use = use;
300Object.defineProperties(AsyncAfWrapperProto.prototype, _objectSpread({}, series, {
301 [Symbol.toStringTag]: {
302 value: 'AsyncAF'
303 }
304}));
305/**
306 * empty AsyncAF class wrapper
307 *
308 * AsyncAfWrapper is one option for cherry-picking only the methods you'd like to use in your code; {@link AsyncAfWrapper#use use}, {@link AsyncAF#series series}, and {@link AsyncAF#io io} are the only methods initially available on AsyncAfWrapper; see example below
309 *
310 * **Note:** while AsyncAfWrapper is a class, it can create instances with or without the `new` keyword
311 *
312 * **Example**
313 *
314 * say you only want to use {@link AsyncAF#mapAF mapAF}, {@link AsyncAF#filterAF filterAF}, {@link AsyncAF#forEachAF forEachAF}, and {@link AsyncAF#logAF logAF} instead of pulling in the entire AsyncAF library
315 *
316 * first, install the separate packages (e.g., for npm):
317 *
318 * `$ npm install --save @async-af/{wrapper,map,filter,foreach,log}`
319 *
320 * or, if on Windows:
321 *
322 * `$ npm install --save @async-af/wrapper @async-af/map @async-af/filter @async-af/foreach @async-af/log`
323 *
324 * then import the packages
325 * ```js
326 * import AsyncAF from '@async-af/wrapper'; // aliasing 'AsyncAfWrapper' as 'AsyncAF'
327 * import mapAF from '@async-af/map';
328 * import filterAF from '@async-af/filter';
329 * import forEachAF from '@async-af/foreach';
330 * import logAF from '@async-af/log';
331 * ```
332 *
333 * _if you'd like to save some vertical screen real estate and cut the imports down to one line, see_ {@tutorial TOO_MANY_IMPORTS}
334 *
335 * then call {@link AsyncAfWrapper#use use}, including all prototype methods you'd like to add to AsyncAfWrapper's prototype in the first argument, `prototypeMethods` and all static methods you'd like to add to AsyncAfWrapper in the second optional argument, `staticMethods`
336 * ```js
337 * AsyncAF.use({ // prototype methods go in the first argument
338 * mapAF,
339 * filterAF,
340 * forEachAF
341 * }, { // static methods go in the second argument
342 * logAF
343 * });
344 * ```
345 *
346 * ready to go!
347 * ```js
348 * const promises = [1, 2, 3].map(n => Promise.resolve(n));
349 *
350 * AsyncAF(promises).mapAF(n => n * 2).filterAF(n => n !== 4).forEachAF(n => console.log(n));
351 * // logs 2 then 6
352 *
353 * AsyncAF.logAF(promises);
354 * // @filename.js:24:9:
355 * // [ 1, 2, 3 ]
356 * // in 0.003 secs
357 * ```
358 *
359 * **protip:** you can use the same technique to add your own custom prototype or static methods to AsyncAfWrapper or even to the main AsyncAF class; see {@link AsyncAfWrapper#use use} for an example
360 * @param {any} data the data to be wrapped by the AsyncAF class; can be promises or non-promises
361 * @returns {Object} returns an instance of AsyncAfWrapper wrapping the passed in data
362 * @since 3.0.0
363 * @see AsyncAF
364 * @see {@link AsyncAfWrapper#use use}
365 * @see {@tutorial TOO_MANY_IMPORTS}
366 * @class AsyncAfWrapper
367 */
368
369const AsyncAfWrapper = createNewlessClass(class AsyncAfWrapper extends AsyncAfWrapperProto {});
370
371/* eslint-disable no-console */
372const wrappedLog = function wrappedLog() {
373 console && console.log && console.log(...arguments);
374};
375
376const wrappedWarn = function wrappedWarn() {
377 console && console.warn && console.warn(...arguments);
378};
379
380/**
381 * Sets logging options for {@link AsyncAF#logAF logAF}
382 *
383 * accepts an options object with the following optional properties:
384 * - label (`Boolean`) - set to `false` to disable logging the location of calls to logAF
385 * - duration (`Boolean`) - set to `false` to disable logging the time it takes (in secs) to complete each call to logAF
386 * - labelFormat (`String`|`Function`) - alters the format of logAF labels; choose between `file` (*default*), `path`, `parent`, `arrow`, or a custom string or function
387 *
388 * ```js
389 * const promise = new Promise(resolve => setTimeout(
390 * () => resolve(1), 1000)
391 * );
392 * ```
393 * **default logging**
394 * ```js
395 * logAF(promise, 2);
396 *
397 * // @filename.js:24:1:
398 * // 1 2
399 * // in 0.998 secs
400 * ```
401 * **turn off label**
402 * ```js
403 * logAF.options({ label: false });
404 * logAF(promise, 2);
405 *
406 * // 1 2
407 * // in 0.999 secs
408 * ```
409 * **turn off duration**
410 * ```js
411 * logAF.options({ duration: false });
412 * logAF(promise, 2);
413 *
414 * // @filename.js:24:1:
415 * // 1 2
416 * ```
417 * **change labelFormat**
418 *
419 * &#9679; file (*default*)
420 *
421 * ```js
422 * logAF.options({ labelFormat: 'file' });
423 * logAF(promise, 2);
424 *
425 * // @filename.js:24:1:
426 * // 1 2
427 * // in 0.998 secs
428 * ```
429 * &#9679; path
430 *
431 * ```js
432 * logAF.options({ labelFormat: 'path' });
433 * logAF(promise, 2);
434 *
435 * // @/Path/to/current/directory/filename.js:24:1:
436 * // 1 2
437 * // in 0.997 secs
438 * ```
439 * &#9679; parent
440 *
441 * ```js
442 * logAF.options({ labelFormat: 'parent' });
443 * logAF(promise, 2);
444 *
445 * // @parentDirectory/filename.js:24:1:
446 * // 1 2
447 * // in 0.998 secs
448 * ```
449 * &#9679; arrow
450 *
451 * ```js
452 * logAF.options({ labelFormat: 'arrow' });
453 * logAF(promise, 2);
454 *
455 * // ========================> 1 2
456 * // in 0.999 secs
457 * ```
458 *
459 * &#9679; custom (create your own labelFormat)
460 * - to set a custom labelFormat, set it to any string other than the formats above
461 *
462 * ```js
463 * logAF.options({
464 * labelFormat: 'I logged this:'
465 * });
466 * logAF(promise, 2);
467 *
468 * // I logged this: 1 2
469 * // in 1.000 secs
470 * ```
471 *
472 * - labelFormat also accepts a function with access to an object containing the location variables `file`, `path`, `parent`, `arrow`, `line`, and `col`
473 *
474 * e.g., to set the labelFormat to `file:line:col =>`:
475 * ```js
476 * logAF.options({
477 * labelFormat: ({file, line, col}) => `${file}:${line}:${col} =>`
478 * });
479 * logAF(promise, 2);
480 *
481 * // filename.js:24:1 => 1 2
482 * // in 0.998 secs
483 * ```
484 *
485 * and just to demonstrate all the location variables in one custom format:
486 * ```js
487 * logAF.options({
488 * labelFormat: ({arrow, line, col, parent, file, path}) =>
489 * `${arrow}
490 * line: ${line}
491 * col: ${col}
492 * parent: ${parent}
493 * file: ${file}
494 * path: ${path}
495 * `
496 * });
497 * logAF(promise, 2);
498 *
499 * // ========================>
500 * // line: 24
501 * // col: 1
502 * // parent: parentDirectory/
503 * // file: filename.js
504 * // path: /Full/path/to/the/parentDirectory/
505 * // 1 2
506 * // in 0.998 secs
507 * ```
508 *
509 * to reset `logAF.options` to its default values, call `logAF.options.reset`
510 * ```js
511 * logAF.options.reset();
512 *
513 * // options are now:
514 * // label: true,
515 * // duration: true,
516 * // labelFormat: 'file'
517 * ```
518 *
519 * @static
520 * @param {Object} options the options for logAF
521 * @param {Boolean} [options.label=true] set to false to turn off the label
522 * @param {Boolean} [options.duration=true] set to false to turn off duration
523 * @param {String|Function} [options.labelFormat=file] see examples for sample label formats
524 * @returns {undefined} sets the options for logAF
525 * @see {@link AsyncAF#logAF logAF}
526 * @see logAF.options.reset to reset options to default
527 * @memberof AsyncAF
528 * @alias AsyncAF#logAF_options
529 */
530
531const logAfOptions = function logAfOptions() {
532 let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
533 const {
534 label,
535 duration,
536 labelFormat
537 } = options;
538 if (typeof label === 'boolean') logAF$1.label = label;
539 if (typeof duration === 'boolean') logAF$1.duration = duration;
540 if (labelFormat) if (typeof labelFormat === 'string' || typeof labelFormat === 'function') logAF$1.labelFormat = labelFormat;else logAF$1.wrappedWarn('Warning: logAF labelFormat option must be set to \'file\' (default), \'path\', \'parent\', \'arrow\', or a custom string or function\n');
541};
542
543function _templateObject4() {
544 const data = _taggedTemplateLiteral(["/"]);
545
546 _templateObject4 = function _templateObject4() {
547 return data;
548 };
549
550 return data;
551}
552
553function _templateObject3() {
554 const data = _taggedTemplateLiteral(["/"]);
555
556 _templateObject3 = function _templateObject3() {
557 return data;
558 };
559
560 return data;
561}
562
563function _templateObject2() {
564 const data = _taggedTemplateLiteral(["/"]);
565
566 _templateObject2 = function _templateObject2() {
567 return data;
568 };
569
570 return data;
571}
572
573function _templateObject() {
574 const data = _taggedTemplateLiteral([":"]);
575
576 _templateObject = function _templateObject() {
577 return data;
578 };
579
580 return data;
581}
582
583const custom = (format, fullPath, arrow) => {
584 if (typeof format === 'string') return format;
585 let [path, line, col] = fullPath.split(_templateObject()); // eslint-disable-line prefer-const
586
587 path = path.split(_templateObject2());
588 const file = path.pop();
589 path = path.join(_templateObject3());
590 const parent = "".concat(path.split(_templateObject4()).pop(), "/");
591 path += '/';
592 return format({
593 path,
594 line,
595 col,
596 file,
597 parent,
598 arrow
599 });
600};
601
602function _templateObject4$1() {
603 const data = _taggedTemplateLiteral(["/"]);
604
605 _templateObject4$1 = function _templateObject4() {
606 return data;
607 };
608
609 return data;
610}
611
612function _templateObject3$1() {
613 const data = _taggedTemplateLiteral(["/"]);
614
615 _templateObject3$1 = function _templateObject3() {
616 return data;
617 };
618
619 return data;
620}
621
622function _templateObject2$1() {
623 const data = _taggedTemplateLiteral(["/"]);
624
625 _templateObject2$1 = function _templateObject2() {
626 return data;
627 };
628
629 return data;
630}
631
632function _templateObject$1() {
633 const data = _taggedTemplateLiteral(["\n"], ["\\n"]);
634
635 _templateObject$1 = function _templateObject() {
636 return data;
637 };
638
639 return data;
640}
641
642const setFormat = labelFormat => {
643 const error = new Error();
644 /* istanbul ignore if */
645
646 if (!error.stack) return '';
647 const [targetLine] = error.stack.split(_templateObject$1()).filter((_, i, lines) => /logAF(?:\s+|\s+\[.+\]\s+)\(/.test(lines[i ? i - 1 : i]) || /logAfStub(?:\s+|\s+\[.+\]\s+)\(/.test(lines[i]));
648 const fullPath = targetLine.slice(targetLine.indexOf(_templateObject2$1())).replace(')', '');
649 const target = fullPath.lastIndexOf(_templateObject3$1());
650 const formats = {
651 file() {
652 return "@".concat(fullPath.slice(target + 1), ":\n");
653 },
654
655 path() {
656 return "@".concat(fullPath, ":\n");
657 },
658
659 parent() {
660 const start = fullPath.slice(0, target).lastIndexOf(_templateObject4$1()) + 1;
661 return "@".concat(fullPath.slice(start), ":\n");
662 },
663
664 arrow() {
665 return '========================>';
666 }
667
668 };
669 return formats[labelFormat] ? formats[labelFormat]() : custom(labelFormat, fullPath, formats.arrow());
670};
671
672/**
673 * logs items to the console in the order given
674 *
675 * if any items are a promise, they will first be resolved in parallel and then logged
676 *
677 * ```js
678 * import { logAF } from 'async-af';
679 *
680 * const promise = new Promise(resolve => setTimeout(
681 * () => resolve(2), 1000)
682 * );
683 *
684 * logAF(1, promise, 3);
685 *
686 * // @filename.js:6:12:
687 * // 1 2 3
688 * // in 0.998 secs
689 * ```
690 *
691 * **Note:** since logAF returns a promise, the items in the previous example would be logged *after* any synchronous calls to `console.log`
692 *
693 * to produce in-order logging with any surrounding calls to `console.log`, `await` logAF:
694 * ```js
695 * logAF.options({ label: false, duration: false });
696 *
697 * (async () => {
698 * console.log(1);
699 * // ...some code
700 * await logAF(promise);
701 * // ...some more code
702 * console.log(3);
703 * })();
704 *
705 * // 1
706 * // 2
707 * // 3
708 * ```
709 *
710 * **experimental feature**: the label may not work correctly in all environments; to turn the label off, set `label` to `false` in {@link AsyncAF#logAF_options logAF.options}, where you can also change the label's format
711 *
712 * @static
713 * @param {any} items The items to print (log to the console)
714 * @returns {Promise<undefined>} returns a `Promise` that logs items to the console
715 * @see log (alias)
716 * @see {@link AsyncAF#logAF_options logAF.options} to turn the label off or change its format
717 * @see logAF.options.reset to reset options to default
718 * @since 3.0.0
719 * @memberof AsyncAF
720 * @alias AsyncAF#logAF
721 */
722
723const logAF = function logAF() {
724 for (var _len = arguments.length, items = new Array(_len), _key = 0; _key < _len; _key++) {
725 items[_key] = arguments[_key];
726 }
727
728 if (logAF.label) items.unshift(logAF.setFormat(logAF.labelFormat));
729 const start = Date.now();
730 return Promise.all(items).then(toLog => {
731 if (logAF.duration) {
732 const end = Date.now();
733 const numberOf = ((end - start) / 1000).toFixed(3);
734 toLog.push("\n in ".concat(numberOf, " secs"));
735 }
736
737 logAF.wrappedLog('', ...toLog);
738 });
739};
740
741Object.defineProperties(logAF, {
742 wrappedLog: {
743 value: wrappedLog,
744 writable: true
745 },
746 wrappedWarn: {
747 value: wrappedWarn,
748 writable: true
749 },
750 setFormat: {
751 value: setFormat,
752 writable: true
753 },
754 options: {
755 value: logAfOptions,
756 writable: true
757 }
758});
759(logAF.options.reset = function logAfOptionsReset() {
760 logAF.label = true;
761 logAF.labelFormat = 'file';
762 logAF.duration = true;
763})();
764var logAF$1 = nameFunction(logAF, 'logAF');
765
766/* eslint-disable no-unused-vars, valid-jsdoc */
767
768const permissiveIsArrayLike = function permissiveIsArrayLike(obj) {
769 return Array.isArray(obj) || obj != null && obj.length != null;
770};
771
772const promiseAllWithHoles = promises => new Promise((resolve, reject) => {
773 const length = promises.length >>> 0;
774 const result = Array(length);
775 let pending = length;
776 let i = length;
777 if (!length) return resolve(result);
778
779 const settlePromise = i => Promise.resolve(promises[i]).then(value => {
780 if (i in promises) result[i] = value;
781 if (! --pending) resolve(result);
782 }, reject);
783
784 while (i--) settlePromise(i);
785});
786
787const serial = arr => function resolveSerially(resolved, i) {
788 const {
789 length
790 } = resolved;
791 if (!length) return Promise.resolve(resolved);
792 const hole = !(i in arr);
793 return Promise.resolve(arr[i]).then(el => {
794 if (!hole) resolved[i] = el;
795 if (i === length - 1) return resolved;
796 return resolveSerially(resolved, i + 1);
797 });
798}(Array(arr.length >>> 0), 0);
799
800const parallel = function parallel(arr, mapper) {
801 let thisArg = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
802 return promiseAllWithHoles(arr, el => el).then(!mapper ? undefined : arr => promiseAllWithHoles(Array.prototype.map.call(arr, mapper, thisArg)));
803};
804
805/**
806 * creates a new `Array` with the results of calling a provided function on every element in the original array
807 *
808 * if any elements are a `Promise`, they will first be resolved in parallel and then processed
809 *
810 * *Note*: if you'd rather resolve and process elements in series, consider using `series.mapAF` or its alias, `io.mapAF`
811 *
812 * @param {callback} callback function that produces an element of the new `Array`
813 *
814 * `callback` accepts three arguments:
815 * - `currentValue` value of the current element being processed in the array
816 * - `index`*`(optional)`* index of `currentValue` in the array
817 * - `array`*`(optional)`* the array that mapAF is being applied to
818 * @param {Object=} thisArg value to use as `this` when executing `callback`
819 * @returns {Promise.<Array>} `Promise` that resolves to a new `Array` with each element being the result of calling `callback` on each original element
820 * @example
821 *
822 * const promises = [1, 2].map(n => Promise.resolve(n));
823 *
824 *
825 * // basic usage
826 * const doubled = AsyncAF(promises).mapAF(el => el * 2);
827 *
828 * console.log(doubled); // Promise that resolves to [2, 4]
829 *
830 * AsyncAF.logAF(doubled); // logs [2, 4]
831 *
832 *
833 * // using .then
834 * AsyncAF(promises).mapAF(el => el * 3).then(tripled => {
835 * console.log(tripled); // logs [3, 6]
836 * });
837 *
838 *
839 * // inside an async function
840 * (async () => {
841 * const quadrupled = await AsyncAF(promises).mapAF(
842 * el => el * 4
843 * );
844 * console.log(quadrupled); // logs [4, 8]
845 * })();
846 * @since 3.0.0
847 * @see map (alias)
848 * @see {@link AsyncAF#series series.mapAF}
849 * @memberof AsyncAF#
850 */
851
852const mapAF = function mapAF(callback$$1) {
853 let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
854 return this.then(arr => {
855 if (!permissiveIsArrayLike(arr)) throw TypeError("mapAF cannot be called on ".concat(arr, ", only on an Array or array-like Object"));
856 if (typeof callback$$1 !== 'function') throw TypeError("".concat(callback$$1, " is not a function"));
857 return this.inSeries ? serial(arr).then(arr => arr.reduce((map, el, i, arr) => map.then(map => {
858 map[i] = Promise.resolve(callback$$1.call(thisArg, el, i, arr));
859 return promiseAllWithHoles(map);
860 }), Promise.resolve(Array(arr.length >>> 0)))) : parallel(arr, callback$$1, thisArg);
861 });
862};
863
864/**
865 * executes a callback function on each element in an array
866 *
867 * if any elements are a `Promise`, they will first be resolved in parallel and then processed
868 *
869 * *Note*: if you'd rather resolve and process elements in series, consider using `series.forEachAF` or its alias, `io.forEachAF`
870 *
871 * @param {callback} callback function to execute for each element
872 *
873 * `callback` accepts three arguments:
874 * - `currentValue` value of the current element being processed in the array
875 * - `index`*`(optional)`* index of `currentValue` in the array
876 * - `array`*`(optional)`* the array that forEachAF is being applied to
877 * @param {Object=} thisArg value to use as `this` when executing `callback`
878 * @returns {Promise.<undefined>} `Promise` that resolves to `undefined`
879 * @example
880 *
881 * const promises = [1, 2].map(n => Promise.resolve(n));
882 *
883 *
884 * AsyncAF(promises).forEachAF(el => {
885 * console.log(el); // logs 1 then 2
886 * });
887 * @since 3.0.0
888 * @see forEach (alias)
889 * @see {@link AsyncAF#series series.forEachAF}
890 * @memberof AsyncAF#
891 */
892
893const forEachAF = function forEachAF(callback$$1) {
894 let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
895 return this.then(arr => {
896 if (!permissiveIsArrayLike(arr)) throw TypeError("forEachAF cannot be called on ".concat(arr, ", only on an Array or array-like Object"));
897 if (typeof callback$$1 !== 'function') throw TypeError("".concat(callback$$1, " is not a function"));
898 return (this.inSeries ? serial(arr).then(arr => arr.reduce((expr, el, i, arr) => expr.then(() => Promise.resolve(callback$$1.call(thisArg, el, i, arr))), Promise.resolve())) : parallel(arr, callback$$1, thisArg)).then(() => {});
899 });
900};
901
902/**
903 * creates a new `Array` with all elements that pass the test implemented by the provided callback function
904 *
905 * if any elements are a `Promise`, they will first be resolved in parallel and then tested
906 *
907 * *Note*: if you'd rather resolve and test elements in series, consider using `series.filterAF` or its alias, `io.filterAF`
908 *
909 * @param {callback} callback function that tests each element of the array; return `true` to keep the element, `false` to filter it out
910 *
911 * `callback` accepts three arguments:
912 * - `currentValue` value of the current element being processed in the array
913 * - `index`*`(optional)`* index of `currentValue` in the array
914 * - `array`*`(optional)`* the array that `filterAF` is being applied to
915 * @param {Object=} thisArg value to use as `this` when executing `callback`
916 * @returns {Promise.<Array>} `Promise` that resolves to a new `Array` with the elements that pass the test; if no elements pass the test, the promise will resolve to an empty array
917 * @example
918 *
919 * const promises = [1, 2, 3].map(n => Promise.resolve(n));
920 *
921 *
922 * // basic usage
923 * const odds = AsyncAF(promises).filterAF(n => n % 2);
924 *
925 * console.log(odds); // Promise that resolves to [1, 3]
926 *
927 * AsyncAF.logAF(odds); // logs [1, 3]
928 *
929 *
930 * // using .then
931 * AsyncAF(promises).filterAF(n => n % 2).then(odds => {
932 * console.log(odds); // logs [1, 3]
933 * });
934 *
935 *
936 * // inside an async function
937 * (async () => {
938 * const odds = await AsyncAF(promises).filterAF(
939 * n => n % 2
940 * );
941 * console.log(odds); // logs [1, 3]
942 * })();
943 * @since 3.0.0
944 * @see filter (alias)
945 * @see {@link AsyncAF#series series.filterAF}
946 * @memberof AsyncAF#
947 */
948
949const filterAF = function filterAF(callback$$1) {
950 let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
951 return this.then(arr => {
952 if (!permissiveIsArrayLike(arr)) throw TypeError("filterAF cannot be called on ".concat(arr, ", only on an Array or array-like Object"));
953 if (typeof callback$$1 !== 'function') throw TypeError("".concat(callback$$1, " is not a function"));
954 return (this.inSeries ? serial : parallel)(arr).then(arr => (this.inSeries ? arr.reduce((bools, el, i, arr) => bools.then(bools => {
955 bools[i] = callback$$1.call(thisArg, el, i, arr);
956 return Promise.all(bools);
957 }), Promise.all([])) : parallel(arr, callback$$1, thisArg)).then(bools => arr.filter((_, i) => bools[i])));
958 });
959};
960
961/* eslint-disable no-unused-vars, valid-jsdoc */
962
963/* eslint-disable prefer-rest-params */
964
965/**
966 * applies a function against an accumulator and each element in an array (from left to right) to reduce it to a single value
967 *
968 * if any elements are a `Promise`, they will first be resolved in parallel and then processed in series
969 *
970 * *Note*: if this behavior is not desirable, consider using `series.reduceAF` or its alias, `io.reduceAF`; that way, if any elements are a `Promise`, they will both be resolved in series _and_ processed in series
971 *
972 * @param {callback} callback function to execute for each element
973 *
974 * `callback` accepts up to four arguments:
975 * - `accumulator` accumulates the callback's return values; the accumulated value previously returned in the last invocation of the callback, or initialValue, if supplied
976 * - `currentValue` value of the current element being processed in the array
977 * - `index`*`(optional)`* index of `currentValue` in the array
978 * - `array`*`(optional)`* the array that `reduceAF` is being applied to
979 * @param {any=} initialValue value to use as the first argument to the first call of the callback; if no initial value is supplied, the first element in the array will be used; note: calling reduceAF on an empty array with no initial value will throw an error
980 * @returns {Promise.<any>} `Promise` that resolves to the reduced value
981 * @example
982 *
983 * const promises = [1, 2, 3].map(n => Promise.resolve(n));
984 *
985 *
986 * // basic usage
987 * const sum = AsyncAF(promises).reduceAF((sum, num) => sum + num);
988 *
989 * console.log(sum); // Promise that resolves to 6
990 *
991 * AsyncAF.logAF(sum); // logs 6
992 *
993 *
994 * // using .then
995 * AsyncAF(promises).reduceAF((sum, num) => sum + num).then(sum => {
996 * console.log(sum); // logs 6
997 * });
998 *
999 *
1000 * // inside an async function
1001 * (async () => {
1002 * const sum = await AsyncAF(promises).reduceAF((sum, num) => sum + num);
1003 * console.log(sum); // logs 6
1004 * })();
1005 *
1006 *
1007 * // using an initial value
1008 * AsyncAF(promises).reduceAF((sum, num) => sum + num, 12) // Promise that resolves to 18
1009 * @since 3.1.0
1010 * @see reduce (alias)
1011 * @see {@link AsyncAF#series series.reduceAF}
1012 * @memberof AsyncAF#
1013 */
1014
1015const reduceAF = function reduceAF(callback
1016/* , initialValue */
1017) {
1018 return this.then(arr => {
1019 if (!permissiveIsArrayLike(arr)) throw TypeError("reduceAF cannot be called on ".concat(arr, ", only on an Array or array-like Object"));
1020 if (typeof callback !== 'function') throw TypeError("".concat(callback, " is not a function"));
1021 const length = arr.length >>> 0;
1022 if (!length && arguments.length === 1) throw TypeError('reduceAF cannot be called on an empty array without an initial value');
1023 if (!length) return arguments[1];
1024
1025 const hole = i => !(i in arr);
1026
1027 let i = 0;
1028 let acc;
1029
1030 if (arguments.length === 2) {
1031 [, acc] = arguments;
1032 } else {
1033 while (hole(i)) i++;
1034
1035 acc = arr[i++];
1036 }
1037
1038 return (this.inSeries ? serial : parallel)(arr).then(arr => {
1039 const reduceAF = (acc, i) => Promise.resolve(acc).then(acc => Promise.resolve(!hole(i) ? callback(acc, arr[i], i, arr) : acc).then(acc => i === length - 1 ? acc : reduceAF(acc, i + 1)));
1040
1041 return reduceAF(acc, i);
1042 });
1043 });
1044};
1045
1046/**
1047 * tests whether all elements in the array pass the test implemented by the provided callback function
1048 *
1049 * if any elements are a `Promise`, they will first be resolved in parallel and then tested
1050 *
1051 * *Note*: since `everyAF` is run in parallel, `callback` will be run on all elements even if one of the first few elements fails the test; if this behavior is not desireable, consider using `series.everyAF` or its alias, `io.everyAF`
1052 *
1053 * @param {callback} callback function that tests each element of the array
1054 *
1055 * `callback` accepts three arguments:
1056 * - `currentValue` value of the current element being processed in the array
1057 * - `index`*`(optional)`* index of `currentValue` in the array
1058 * - `array`*`(optional)`* the array that `everyAF` is being applied to
1059 * @param {Object=} thisArg value to use as `this` when executing `callback`
1060 * @returns {Promise.<Boolean>} `Promise` that resolves to `true` if the callback function returns a truthy value for every array element; otherwise, `false`
1061 * @example
1062 *
1063 * const promises = [1, 2, 3].map(n => Promise.resolve(n));
1064 *
1065 *
1066 * // basic usage
1067 * const allAreOdd = AsyncAF(promises).everyAF(n => n % 2);
1068 *
1069 * console.log(allAreOdd); // Promise that resolves to false
1070 *
1071 * AsyncAF.logAF(allAreOdd); // logs false
1072 *
1073 *
1074 * // using .then
1075 * AsyncAF(promises).everyAF(n => n % 2).then(allAreOdd => {
1076 * console.log(allAreOdd); // logs false
1077 * });
1078 *
1079 *
1080 * // inside an async function
1081 * (async () => {
1082 * const allAreNums = await AsyncAF(promises).everyAF(
1083 * n => typeof n === 'number'
1084 * );
1085 * console.log(allAreNums); // logs true
1086 * })();
1087 * @since 3.2.0
1088 * @see every (alias)
1089 * @see {@link AsyncAF#series series.everyAF}
1090 * @memberof AsyncAF#
1091 */
1092
1093const everyAF = function everyAF(callback$$1) {
1094 let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
1095 return this.then(arr => {
1096 if (!permissiveIsArrayLike(arr)) throw TypeError("everyAF cannot be called on ".concat(arr, ", only on an Array or array-like Object"));
1097 if (typeof callback$$1 !== 'function') throw TypeError("".concat(callback$$1, " is not a function"));
1098 const length = arr.length >>> 0;
1099 return this.inSeries ? !length && true || function seriesEveryAF(arr, i) {
1100 const hole = !(i in arr);
1101 return Promise.resolve(arr[i]).then(el => {
1102 arr[i] = el;
1103 return Promise.resolve(!hole && callback$$1.call(thisArg, el, i, arr)).then(bool => {
1104 if (!bool && !hole) return false;
1105 if (i === length - 1) return true;
1106 return seriesEveryAF(arr, i + 1);
1107 });
1108 });
1109 }(Array.prototype.slice.call(arr), 0) : parallel(arr, callback$$1, thisArg).then(bools => bools.every(Boolean));
1110 });
1111};
1112
1113/**
1114 * tests whether at least one element in the array passes the test implemented by the provided callback function
1115 *
1116 * if any elements are a `Promise`, they will first be resolved in parallel and then tested
1117 *
1118 * *Note*: since `someAF` is run in parallel, `callback` will be run on all elements even if one of the first few elements passes the test; if this behavior is not desireable, consider using `series.someAF` or its alias, `io.someAF`
1119 *
1120 * @param {callback} callback function that tests each element of the array
1121 *
1122 * `callback` accepts three arguments:
1123 * - `currentValue` value of the current element being processed in the array
1124 * - `index`*`(optional)`* index of `currentValue` in the array
1125 * - `array`*`(optional)`* the array that `someAF` is being applied to
1126 * @param {Object=} thisArg value to use as `this` when executing `callback`
1127 * @returns {Promise.<Boolean>} `Promise` that resolves to `true` if the callback function returns a truthy value for any array element; otherwise, `false`
1128 * @example
1129 *
1130 * const promises = [1, 2, 3].map(n => Promise.resolve(n));
1131 *
1132 *
1133 * // basic usage
1134 * const someAreEven = AsyncAF(promises).someAF(n => n % 2 === 0);
1135 *
1136 * console.log(someAreEven); // Promise that resolves to true
1137 *
1138 * AsyncAF.logAF(someAreEven); // logs true
1139 *
1140 *
1141 * // using .then
1142 * AsyncAF(promises).someAF(n => n % 2 === 0).then(someAreEven => {
1143 * console.log(someAreEven); // logs true
1144 * });
1145 *
1146 *
1147 * // inside an async function
1148 * (async () => {
1149 * const someAreStrings = await AsyncAF(promises).someAF(
1150 * n => typeof n === 'string'
1151 * );
1152 * console.log(someAreStrings); // logs false
1153 * })();
1154 * @since 3.3.0
1155 * @see some (alias)
1156 * @see {@link AsyncAF#series series.someAF}
1157 * @memberof AsyncAF#
1158 */
1159
1160const someAF = function someAF(callback$$1) {
1161 let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
1162 return this.then(arr => {
1163 if (!permissiveIsArrayLike(arr)) throw TypeError("someAF cannot be called on ".concat(arr, ", only on an Array or array-like Object"));
1164 if (typeof callback$$1 !== 'function') throw TypeError("".concat(callback$$1, " is not a function"));
1165 const length = arr.length >>> 0;
1166 return this.inSeries ? (length || false) && function seriesSomeAF(arr, i) {
1167 const hole = !(i in arr);
1168 return Promise.resolve(arr[i]).then(el => {
1169 arr[i] = el;
1170 return Promise.resolve(!hole && callback$$1.call(thisArg, el, i, arr)).then(bool => {
1171 if (bool && !hole) return true;
1172 if (i === length - 1) return false;
1173 return seriesSomeAF(arr, i + 1);
1174 });
1175 });
1176 }(Array.prototype.slice.call(arr), 0) : parallel(arr, callback$$1, thisArg).then(bools => bools.some(Boolean));
1177 });
1178};
1179
1180const sameValueZero = (a, b) => a === b || Number.isNaN(a) && Number.isNaN(b);
1181/**
1182 * determines whether an array, string, or array-like object includes a certain element or string, returning true or false as appropriate
1183 *
1184 * *Note*: when called on an array or array-like object, `includesAF` is run in parallel and all elements will be resolved even if one of the first few elements is a match; if this behavior is not desireable, consider using `series.includesAF` or its alias, `io.includesAF`
1185 *
1186 * @param {any} searchItem the element or string to search for
1187 * @param {Number=} fromIndex the index at which to begin searching for `searchItem`; a negative value searches from the index of `array/string.length - fromIndex`; defaults to `0`
1188 * @returns {Promise.<Boolean>} `Promise` that resolves to `true` if `searchItem` is found; otherwise, `false`
1189 * @example
1190 *
1191 * // includesAF on an array of promises
1192 * const nums = [1, 2, 3].map(n => Promise.resolve(n));
1193 *
1194 * AsyncAF(nums).includesAF(2); // Promise that resolves to true
1195 *
1196 * AsyncAF(nums).includesAF(5); // Promise that resolves to false
1197 *
1198 * AsyncAF(nums).includesAF(1, 1); // Promise that resolves to false
1199 *
1200 * AsyncAF(nums).includesAF(3, -1); // Promise that resolves to true
1201 *
1202 * // includesAF on a promise-wrapped string
1203 * const string = Promise.resolve('test string');
1204 *
1205 * AsyncAF(string).includesAF('test'); // Promise that resolves to true
1206 *
1207 * AsyncAF(string).includesAF('nope'); // Promise that resolves to false
1208 *
1209 * AsyncAF(string).includesAF('test', 5); // Promise that resolves to false
1210 *
1211 * AsyncAF(string).includesAF('string', -6); // Promise that resolves to true
1212 *
1213 * // includesAF on an array-like object
1214 * (async function () {
1215 * if (await AsyncAF(arguments).includesAF(2)) {
1216 * console.log('2 is included');
1217 * }
1218 * })(1, 2, 3); // logs '2 is included'
1219 *
1220 * @since 3.4.0
1221 * @see includes (alias)
1222 * @see {@link AsyncAF#series series.includesAF}
1223 * @memberof AsyncAF#
1224 */
1225
1226
1227const includesAF = function includesAF(searchItem) {
1228 let fromIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
1229 return this.then(arrOrStr => {
1230 if (!permissiveIsArrayLike(arrOrStr)) throw TypeError("includesAF cannot be called on ".concat(arrOrStr, ", only on an Array, String, or array-like Object"));
1231 const length = arrOrStr.length >>> 0;
1232 const fromIdx = fromIndex | 0;
1233 return typeof arrOrStr === 'string' ? arrOrStr.includes(searchItem, fromIdx) : this.inSeries ? (length || false) && function seriesIncludesAF(i) {
1234 return Promise.resolve(arrOrStr[i]).then(el => {
1235 if (sameValueZero(el, searchItem)) return true;
1236 if (i >= length - 1) return false;
1237 return seriesIncludesAF(i + 1);
1238 });
1239 }(Math.max(fromIdx >= 0 ? fromIdx : length - Math.abs(fromIdx), 0)) : parallel(arrOrStr).then(arr => arr.includes(searchItem, fromIdx));
1240 });
1241};
1242
1243/**
1244 * resolves to the value of the first element in the array that satisfies the provided callback function; otherwise, `undefined`
1245 *
1246 * *Note*: since `findAF` is run in parallel, `callback` will be run on all elements even if one of the first few elements passes the test; if this behavior is not desireable, consider using `series.findAF` or its alias, `io.findAF`
1247 *
1248 * @param {callback} callback function to test each element in the array
1249 *
1250 * `callback` accepts three arguments:
1251 * - `currentValue` value of the current element being processed in the array
1252 * - `index`*`(optional)`* index of `currentValue` in the array
1253 * - `array`*`(optional)`* the array that findAF is being applied to
1254 * @param {Object=} thisArg value to use as `this` when executing `callback`
1255 * @returns {Promise.<any>} `Promise` that resolves to the first element in the array that passes the test; otherwise, undefined
1256 * @example
1257 *
1258 * const inventory = [
1259 * {name: 'nuts', quantity: 2000},
1260 * {name: 'bolts', quantity: 5000},
1261 * {name: 'screws', quantity: 9001}
1262 * ].map(part => Promise.resolve(part));
1263 *
1264 * AsyncAF(inventory).findAF(part => part.name === 'screws');
1265 * // Promise that resolves to {name: 'screws', quantity: 9001}
1266 * @since 3.5.0
1267 * @see find (alias)
1268 * @see {@link AsyncAF#series series.findAF}
1269 * @memberof AsyncAF#
1270 */
1271
1272const findAF = function findAF(callback$$1) {
1273 let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
1274 return this.then(arr => {
1275 if (!permissiveIsArrayLike(arr)) throw TypeError("findAF cannot be called on ".concat(arr, ", only on an Array or array-like Object"));
1276 if (typeof callback$$1 !== 'function') throw TypeError("".concat(callback$$1, " is not a function"));
1277 const filled = Array.from(arr);
1278 const length = filled.length >>> 0;
1279 return this.inSeries ? (length || undefined) && function seriesFindAF(arr, i) {
1280 return Promise.resolve(arr[i]).then(el => {
1281 arr[i] = el;
1282 return Promise.resolve(callback$$1.call(thisArg, el, i, arr)).then(bool => {
1283 if (bool) return el;
1284 if (i === length - 1) return;
1285 return seriesFindAF(arr, i + 1);
1286 });
1287 });
1288 }(filled, 0) : parallel(filled, callback$$1, thisArg).then(bools => arr[bools.indexOf(true)]);
1289 });
1290};
1291
1292/**
1293 * resolves to the index of the first element in the array that satisfies the provided callback function; otherwise, `-1`
1294 *
1295 * *Note*: since `findIndexAF` is run in parallel, `callback` will be run on all indices even if one of the first few indices passes the test; if this behavior is not desireable, consider using `series.findIndexAF` or its alias, `io.findIndexAF`
1296 *
1297 * @param {callback} callback function to test each element in the array
1298 *
1299 * `callback` accepts three arguments:
1300 * - `currentValue` value of the current element being processed in the array
1301 * - `index`*`(optional)`* index of `currentValue` in the array
1302 * - `array`*`(optional)`* the array that findIndexAF is being applied to
1303 * @param {Object=} thisArg value to use as `this` when executing `callback`
1304 * @returns {Promise.<Number>} `Promise` that resolves to the index of the first element in the array that passes the test; otherwise, `-1`
1305 * @example
1306 *
1307 * const inventory = [
1308 * {name: 'nuts', quantity: 2000},
1309 * {name: 'bolts', quantity: 5000},
1310 * {name: 'screws', quantity: 9001}
1311 * ].map(part => Promise.resolve(part));
1312 *
1313 * AsyncAF(inventory).findIndexAF(part => part.name === 'screws');
1314 * // Promise that resolves to 2
1315 * @since 3.5.0
1316 * @see findIndex (alias)
1317 * @see {@link AsyncAF#series series.findIndexAF}
1318 * @memberof AsyncAF#
1319 */
1320
1321const findIndexAF = function findIndexAF(callback$$1) {
1322 let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
1323 return this.then(arr => {
1324 if (!permissiveIsArrayLike(arr)) throw TypeError("findIndexAF cannot be called on ".concat(arr, ", only on an Array or array-like Object"));
1325 if (typeof callback$$1 !== 'function') throw TypeError("".concat(callback$$1, " is not a function"));
1326 const filled = Array.from(arr);
1327 const length = filled.length >>> 0;
1328 return this.inSeries ? !length && -1 || function seriesFindIndexAF(arr, i) {
1329 return Promise.resolve(arr[i]).then(el => {
1330 arr[i] = el;
1331 return Promise.resolve(callback$$1.call(thisArg, el, i, arr)).then(bool => {
1332 if (bool) return i;
1333 if (i === length - 1) return -1;
1334 return seriesFindIndexAF(arr, i + 1);
1335 });
1336 });
1337 }(filled, 0) : parallel(filled, callback$$1, thisArg).then(bools => bools.indexOf(true));
1338 });
1339};
1340
1341/**
1342 * resolves to the first index of the specified element or string in an array, string, or array-like object, starting the search at `fromIndex`; if the value is not found, resolves to `-1`
1343 *
1344 * *Note*: when called on an array or array-like object, `indexOfAF` is run in parallel and all elements will be resolved even if one of the first few indices is a match; if this behavior is not desireable, consider using `series.indexOfAF` or its alias, `io.indexOfAF`
1345 *
1346 * @param {any} searchItem the element or string to search for
1347 * @param {Number=} fromIndex the index at which to begin searching for `searchItem`; a negative value searches from the index of `array/string.length - fromIndex`; defaults to `0`
1348 * @returns {Promise.<Number>} `Promise` that resolves to the index of `searchItem` if found; otherwise, `-1`
1349 * @example
1350 *
1351 * // indexOfAF on an array of promises
1352 * const nums = [1, 2, 3].map(n => Promise.resolve(n));
1353 *
1354 * AsyncAF(nums).indexOfAF(2); // Promise that resolves to 1
1355 *
1356 * AsyncAF(nums).indexOfAF(5); // Promise that resolves to -1
1357 *
1358 * AsyncAF(nums).indexOfAF(1, 1); // Promise that resolves to -1
1359 *
1360 * AsyncAF(nums).indexOfAF(3, -1); // Promise that resolves to 2
1361 *
1362 * // indexOfAF on a promise-wrapped string
1363 * const string = Promise.resolve('test string');
1364 *
1365 * AsyncAF(string).indexOfAF('test'); // Promise that resolves to 0
1366 *
1367 * AsyncAF(string).indexOfAF('nope'); // Promise that resolves to -1
1368 *
1369 * AsyncAF(string).indexOfAF('test', 5); // Promise that resolves to -1
1370 *
1371 * AsyncAF(string).indexOfAF('string', -6); // Promise that resolves to 5
1372 *
1373 * // indexOfAF on an array-like object
1374 * (async function () {
1375 * if (await AsyncAF(arguments).indexOfAF(2) > -1) {
1376 * console.log('2 is included');
1377 * }
1378 * })(1, 2, 3); // logs '2 is included'
1379 *
1380 * @since 3.5.0
1381 * @see indexOf (alias)
1382 * @see {@link AsyncAF#series series.indexOfAF}
1383 * @memberof AsyncAF#
1384 */
1385
1386const indexOfAF = function indexOfAF(searchItem) {
1387 let fromIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
1388 return this.then(arrOrStr => {
1389 if (!permissiveIsArrayLike(arrOrStr)) throw TypeError("indexOfAF cannot be called on ".concat(arrOrStr, ", only on an Array, String, or array-like Object"));
1390 const length = arrOrStr.length >>> 0;
1391 const fromIdx = fromIndex | 0;
1392 return typeof arrOrStr === 'string' ? arrOrStr.indexOf(searchItem, fromIdx) : this.inSeries ? function seriesIndexOfAF(i) {
1393 return Promise.resolve(arrOrStr[i]).then(el => {
1394 if (i in arrOrStr && el === searchItem) return i;
1395 if (i >= length - 1) return -1;
1396 return seriesIndexOfAF(i + 1);
1397 });
1398 }(Math.max(fromIdx >= 0 ? fromIdx : length - Math.abs(fromIdx), 0)) : parallel(arrOrStr).then(arr => arr.indexOf(searchItem, fromIdx));
1399 });
1400};
1401
1402/**
1403 * resolves to the last index of the specified element or string, searching backwards in an array, string, or array-like object; `fromIndex` offsets the start of the search; if the value is not found, resolves to `-1`
1404 *
1405 * *Note*: when called on an array or array-like object, `lastIndexOfAF` is run in parallel and all elements will be resolved even if one of the last few indices is a match; if this behavior is not desireable, consider using `series.lastIndexOfAF` or its alias, `io.lastIndexOfAF`
1406 *
1407 * @param {any} searchItem the element or string to search for
1408 * @param {Number=} fromIndex the index at which to begin searching backwards for `searchItem`; a negative value searches from the index of `array/string.length - fromIndex`; defaults to `array/string.length - 1`
1409 * @returns {Promise.<Number>} `Promise` that resolves to the last index of `searchItem` if found; otherwise, `-1`
1410 * @example
1411 *
1412 * // lastIndexOfAF on an array of promises
1413 * const nums = [1, 1, 2, 2, 3, 3].map(n => Promise.resolve(n));
1414 *
1415 * AsyncAF(nums).lastIndexOfAF(2); // Promise that resolves to 3
1416 *
1417 * AsyncAF(nums).lastIndexOfAF(5); // Promise that resolves to -1
1418 *
1419 * AsyncAF(nums).lastIndexOfAF(2, -4); // Promise that resolves to 2
1420 *
1421 * AsyncAF(nums).lastIndexOfAF(3, -3); // Promise that resolves to -1
1422 *
1423 * // lastIndexOfAF on a promise-wrapped string
1424 * const string = Promise.resolve('test string to test');
1425 *
1426 * AsyncAF(string).lastIndexOfAF('test'); // Promise that resolves to 15
1427 *
1428 * AsyncAF(string).lastIndexOfAF('nope'); // Promise that resolves to -1
1429 *
1430 * AsyncAF(string).lastIndexOfAF('test', -5); // Promise that resolves to 0
1431 *
1432 * AsyncAF(string).lastIndexOfAF('to', -7); // Promise that resolves to -1
1433 *
1434 * // lastIndexOfAF on an array-like object
1435 * (async function () {
1436 * const lastIndexOf2 = await AsyncAF(arguments).lastIndexOfAF(2);
1437 * console.log(`the last index of 2 in the arguments array-like object is ${lastIndexOf2}`)
1438 * })(1, 1, 2, 2, 3, 3); // the last index of 2 in the arguments array-like object is 3
1439 *
1440 * @since 3.6.0
1441 * @see lastIndexOf (alias)
1442 * @see {@link AsyncAF#series series.lastIndexOfAF}
1443 * @memberof AsyncAF#
1444 */
1445
1446const lastIndexOfAF = function lastIndexOfAF(searchItem) {
1447 let fromIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
1448 return this.then(arrOrStr => {
1449 if (!permissiveIsArrayLike(arrOrStr)) throw TypeError("lastIndexOfAF cannot be called on ".concat(arrOrStr, ", only on an Array, String, or array-like Object"));
1450 const len = arrOrStr.length >>> 0;
1451 let fromIdx = Number(fromIndex);
1452 if (Number.isNaN(fromIdx)) fromIdx = len - 1;
1453 return typeof arrOrStr === 'string' ? arrOrStr.lastIndexOf(searchItem, fromIdx) : this.inSeries ? function seriesLastIndexOfAF(i) {
1454 return Promise.resolve(arrOrStr[i]).then(el => {
1455 if (i in arrOrStr && el === searchItem) return i;
1456 if (i <= 0) return -1;
1457 return seriesLastIndexOfAF(i - 1);
1458 });
1459 }(Math.min(fromIdx >= 0 ? fromIdx : Math.max(len - Math.abs(fromIdx), 0), len - 1)) : parallel(arrOrStr).then(arr => arr.lastIndexOf(searchItem, fromIdx));
1460 });
1461};
1462
1463/**
1464 * joins all elements of an array or array-like object into a string and resolves to that string
1465 *
1466 * @param {any} separator the string that separates each element in the resulting string; defaults to `','`; non-string separators will be converted to strings if necessary; if `separator` is an empty string `''`, the array elements are joined without any characters between them
1467 * @returns {Promise.<String>} `Promise` that resolves to a string with all array elements joined; if array.length is `0`, an empty string `''` is returned
1468 * @example
1469 *
1470 * const animals = ['cow', 'chicken', 'cat', 'dog'].map(a => Promise.resolve(a));
1471 *
1472 * // joinAF separator defaults to ','
1473 * AsyncAF(animals).joinAF(); // Promise that resolves to 'cow,chicken,cat,dog'
1474 *
1475 * // specifying separator
1476 * AsyncAF(animals).joinAF(' & '); // Promise that resolves to 'cow & chicken & cat & dog'
1477 *
1478 * // a non-string separator will be converted to a string
1479 * AsyncAF(animals).joinAF(2); // Promise that resolves to 'cow2chicken2cat2dog'
1480 *
1481 * // empty string separator
1482 * AsyncAF(animals).joinAF(''); // Promise that resolves to 'cowchickencatdog'
1483 *
1484 * // joining an empty array resolves to an empty string
1485 * AsyncAF([]).joinAF('+'); // Promise that resolves to ''
1486 *
1487 * // joinAF on an array-like object
1488 * (async function () {
1489 * const list = await AsyncAF(arguments).joinAF(' - ');
1490 * console.log(`Shopping List: ${list}`);
1491 * })('eggs', 'milk', 'butter', 'pancake mix');
1492 * // Shopping List: eggs - milk - butter - pancake mix
1493 *
1494 * @since 3.6.0
1495 * @see join (alias)
1496 * @memberof AsyncAF#
1497 */
1498
1499const joinAF = function joinAF() {
1500 let separator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ',';
1501 return this.then(arr => {
1502 if (!permissiveIsArrayLike(arr)) throw TypeError("joinAF cannot be called on ".concat(arr, ", only on an Array or array-like Object"));
1503 return parallel(arr).then(arr => arr.join(separator));
1504 });
1505};
1506
1507Object.defineProperty(joinAF, 'length', {
1508 value: 1
1509});
1510
1511const resolveByType = data => Promise[Array.isArray(data) ? 'all' : 'resolve'](data);
1512/**
1513 * merges an array or string with one or more arrays, strings, or other values, and resolves to a new array or string;
1514 *
1515 * concatAF flexibly accepts arrays, strings, promises, other values, or other instances of AsyncAF; see examples
1516 *
1517 * @param {any} values arrays, strings, or values to concatenate into a new array or string
1518 * - if any values are a Promise, they'll first be resolved then concatenated
1519 * - if any values are an instance of AsyncAF, they'll be merged into one instance
1520 * @returns {Promise.<Array>|Promise.<String>} `Promise` that resolves to a newly merged array or string
1521 * @example
1522 *
1523 * // arrays
1524 * const arr = Promise.resolve([1, 2]);
1525 * AsyncAF(arr).concatAF([3, 4]); // Promise that resolves to [1, 2, 3, 4]
1526 * AsyncAF(arr).concatAF([3, 4], [5, 6]); // Promise that resolves to [1, 2, 3, 4, 5, 6]
1527 *
1528 * // nested arrays
1529 * const arr1 = Promise.resolve([1, 2]);
1530 * const arr2 = [3, [4, 5]];
1531 * AsyncAF(arr1).concatAF(arr2); // Promise that resolves to [1, 2, 3, [4, 5]]
1532 *
1533 * // strings
1534 * const str = Promise.resolve('str');
1535 * AsyncAF(str).concatAF('ing'); // Promise that resolves to 'string'
1536 * AsyncAF(str).concatAF('ing', 'y'); // Promise that resolves to 'stringy'
1537 *
1538 * // other instances of AsyncAF
1539 * const aaf1 = AsyncAF([1, 2]);
1540 * const aaf2 = AsyncAF(3);
1541 *
1542 * aaf1.concatAF(aaf2); // Promise that resolves to [1, 2, 3];
1543 *
1544 * const aaf3 = AsyncAF('stringy');
1545 * const aaf4 = AsyncAF('AF');
1546 *
1547 * aaf3.concatAF(aaf4); // Promise that resolves to 'stringyAF'
1548 *
1549 * // promises
1550 * const [p1, p2, p3] = [[1, 2], 3, [4, [5, 6]]].map(v => Promise.resolve(v));
1551 *
1552 * AsyncAF(p1).concatAF(p2); // Promise that resolves to [1, 2, 3]
1553 * AsyncAF(p1).concatAF(p2, p3) // Promise that resolves to [1, 2, 3, 4, [5, 6]]
1554 *
1555 * const pStr1 = Promise.resolve('str');
1556 * const pStr2 = Promise.resolve('ing');
1557 * const pStr3 = Promise.resolve('y');
1558 *
1559 * AsyncAF(pStr1).concatAF(pStr2); // Promise that resolves to 'string'
1560 * AsyncAF(pStr1).concatAF(pStr2, pStr3); // Promise that resolves to 'stringy'
1561 *
1562 * // Note: concatAF will not resolve deeply nested promises; if you need this behavior, concatAF can be used in a
1563 * // function; for example, this function recursively flattens an array of promises
1564 *
1565 * const flattenAsync = arr => AsyncAF(arr).reduceAF(async (acc, val) => {
1566 * return Array.isArray(await val)
1567 * ? AsyncAF(acc).concatAF(flattenAsync(val))
1568 * : AsyncAF(acc).concatAF(val), []);
1569 * };
1570 *
1571 * @since 5.3.0
1572 * @see concat (alias)
1573 * @memberof AsyncAF#
1574 */
1575
1576
1577const concatAF = function concatAF() {
1578 for (var _len = arguments.length, values = new Array(_len), _key = 0; _key < _len; _key++) {
1579 values[_key] = arguments[_key];
1580 }
1581
1582 const isThenable = value => [Promise, this.constructor].some(thenable => value instanceof thenable);
1583
1584 const concat = (arrOrStr, value) => isThenable(value) && value.then(resolveByType).then(value => arrOrStr.concat(value)) || arrOrStr.concat(value);
1585
1586 return this.then(arrOrStr => {
1587 if (!(typeof arrOrStr === 'string' || Array.isArray(arrOrStr))) throw TypeError("concatAF cannot be called on ".concat(arrOrStr, ", only on an Array or String"));
1588 return resolveByType(arrOrStr).then(arrOrStr => values.reduce((arrOrStr, value) => isThenable(arrOrStr) ? arrOrStr.then(arrOrStr => concat(arrOrStr, value)) : concat(arrOrStr, value), arrOrStr));
1589 });
1590};
1591
1592/**
1593 * splits a string into an array of substrings, using a specified separator to determine where to make each split
1594 *
1595 * @param {String|RegExp=} separator a string or regular expression that denotes the points at which each split should occur
1596 * - if a plain-text separator contains more than one character, the entire separator must be found to represent a split point
1597 * - if separator is omitted or does not occur in the string, the array returned will contain one element consisting of the entire string
1598 * - if separator is an empty string, the string is converted to an array of individual characters
1599 * - if separator is a regular expression, the array returned will also contain any separators found as a result of matches within capturing parentheses
1600 * @param {Number=} limit integer specifying a limit on the number of elements to be included in the result; when provided, the string is split at each occurrence of the specified separator but stops including elements when the limit is reached (or the end of the string, if reached sooner); any left-over text is not included in the result
1601 * @returns {Promise.<String[]>} `Promise` that resolves to an array of strings, split at each point the separator occurs in the given string
1602 * @example
1603 *
1604 * // basic usage
1605 * const str = Promise.resolve('s; p; l; i; t');
1606 *
1607 * AsyncAF(str).splitAF('; '); // Promise that resolves to ['s', 'p', 'l', 'i', 't']
1608 *
1609 * // no separator specified or separator not found
1610 * const str = Promise.resolve('splat');
1611 *
1612 * AsyncAF(str).splitAF(); // Promise that resolves to ['splat']
1613 * AsyncAF(str).splitAF('n/a'); // Promise that resolves to ['splat']
1614 *
1615 * // split to individual characters
1616 * const str = Promise.resolve('splitAF');
1617 *
1618 * AsyncAF(str).splitAF(''); // Promise that resolves to ['s', 'p', 'l', 'i', 't', 'A', 'F']
1619 *
1620 * // split on a regular expression
1621 * const str = Promise.resolve('splittedAF');
1622 *
1623 * AsyncAF(str).splitAF(/sp|ted/); // Promise that resolves to ['', 'lit', 'AF']
1624 *
1625 * // and w/ capturing parentheses
1626 *
1627 * AsyncAF(str).splitAF(/(lit|AF)/); // Promise that resolves to ['sp', 'lit', '', 'AF', '']
1628 *
1629 * // setting limit
1630 * const str = Promise.resolve('splitted');
1631 *
1632 * AsyncAF(str).splitAF('', 5); // Promise that resolves to ['s', 'p', 'l', 'i', 't']
1633 * AsyncAF(str).splitAF('', 12); // Promise that resolves to ['s', 'p', 'l', 'i', 't', 't', 'e', 'd']
1634 * AsyncAF(str).splitAF('', 0); // Promise that resolves to []
1635 *
1636 * @since 5.1.0
1637 * @see split (alias)
1638 * @memberof AsyncAF#
1639 */
1640const splitAF = function splitAF() {
1641 let separator = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
1642 let limit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
1643 return this.then(str => {
1644 if (typeof str !== 'string' || Array.isArray(str)) throw TypeError("splitAF may be called on a string but was called on ".concat(str));
1645 return String.prototype.split.call(str, separator, limit);
1646 });
1647};
1648
1649/* eslint-disable import/first */
1650
1651const libName = 'async-af';
1652const libPath = './lib/';
1653
1654const makeScoped = name => "@".concat(libName, "/").concat(name.replace(/AsyncAf|AF/g, '').toLowerCase());
1655/* ____________________________
1656 | CLASSES |
1657 |____________________________| */
1658
1659
1660const classPath = "".concat(libPath, "classes/");
1661const classes = [[{
1662 name: 'AsyncAF'
1663}, "".concat(classPath, "AsyncAF"), libName], [{
1664 name: 'AsyncAfWrapper'
1665}, "".concat(classPath, "AsyncAfWrapper"), makeScoped('AsyncAfWrapper')]];
1666const staticMethods = [nameFunction(logAF$1, 'logAF')].map(method => [method, "".concat(libPath, "methods/other/").concat(method.name), makeScoped(method.name)]);
1667const arrayMethods = [nameFunction(mapAF, 'mapAF'), nameFunction(forEachAF, 'forEachAF'), nameFunction(filterAF, 'filterAF'), nameFunction(reduceAF, 'reduceAF'), nameFunction(everyAF, 'everyAF'), nameFunction(someAF, 'someAF'), nameFunction(includesAF, 'includesAF'), nameFunction(findAF, 'findAF'), nameFunction(findIndexAF, 'findIndexAF'), nameFunction(indexOfAF, 'indexOfAF'), nameFunction(lastIndexOfAF, 'lastIndexOfAF'), nameFunction(joinAF, 'joinAF'), nameFunction(concatAF, 'concatAF')].map(method => [method, "".concat(libPath, "methods/arrays/").concat(method.name), makeScoped(method.name)]); // strings
1668const stringMethods = [nameFunction(splitAF, 'splitAF')].map(method => [method, "".concat(libPath, "methods/strings/").concat(method.name), makeScoped(method.name)]);
1669const prototypeMethods = [...arrayMethods, ...stringMethods];
1670/* ____________________________
1671 | COLLECTIONS |
1672 |____________________________| */
1673// import arrays from './lib/collections/arrays';
1674
1675const collections = [// arrays,
1676];
1677[...classes, ...staticMethods, ...prototypeMethods, ...collections];
1678
1679const pluckMethods = packages => packages.map((_ref) => {
1680 let [method] = _ref;
1681 return method;
1682});
1683
1684const staticMethodsOnly = pluckMethods(staticMethods);
1685const prototypeMethodsOnly = pluckMethods(prototypeMethods);
1686
1687function _templateObject$2() {
1688 const data = _taggedTemplateLiteral(["AF"]);
1689
1690 _templateObject$2 = function _templateObject() {
1691 return data;
1692 };
1693
1694 return data;
1695}
1696/**
1697 * class that holds all the AsyncAF methods
1698 *
1699 * while AsyncAF is a class, it can create instances with or without the `new` keyword
1700 * @param {any} data the data to be wrapped by the AsyncAF class; can be promises or non-promises
1701 * @returns {Object} returns an instance of AsyncAF wrapping the passed in data
1702 * @example
1703 *
1704 * const promises = [1, 2, 3].map(n => Promise.resolve(n));
1705 *
1706 *
1707 * AsyncAF(promises).mapAF(n => n * 2).filterAF(n => n !== 4).forEachAF(n => console.log(n));
1708 * // logs 2 then 6
1709 * @since 3.0.0
1710 * @see AsyncAfWrapper
1711 * @class AsyncAF
1712 */
1713
1714const AsyncAF = createNewlessClass(class AsyncAF extends AsyncAfWrapperProto {});
1715
1716const prepForDefine = methods => methods.reduce((methods, method) => {
1717 // add all '*AF' methods and add 'AF-less' aliases (e.g., mapAF -> map)
1718 const [alias] = method.name.split(_templateObject$2()) ||
1719 /* istanbul ignore next */
1720 [method.name];
1721 return Object.assign(methods, {
1722 [method.name]: {
1723 value: method
1724 }
1725 }, {
1726 [alias]: {
1727 value: method
1728 }
1729 });
1730}, {});
1731
1732Object.defineProperties(AsyncAfWrapperProto, prepForDefine(staticMethodsOnly));
1733Object.defineProperties(AsyncAfWrapperProto.prototype, prepForDefine(prototypeMethodsOnly));
1734
1735export default AsyncAF;
1736//# sourceMappingURL=index.js.map