async/iterators.js

  1. /**
  2. * Asynchronous tools.
  3. *
  4. * @module async
  5. * @license Apache-2.0
  6. * @copyright Mat. 2018-present
  7. */
  8. import { head } from "../array/list";
  9. import { curry } from "../func/curry";
  10. import { inc } from "../math/arithmetic";
  11. import { isArray, isFunction } from "../type/check";
  12. import { btquote } from "../utils/misc";
  13. /**
  14. * Asynchronous version of standard `Array.prototype.map` function.
  15. *
  16. * - `arr` - array to operate on
  17. * - `f` - async or sync function with signature:
  18. * - `this` - bound to `arr`
  19. * - `element` - currently processed element
  20. * - `index` - current index
  21. *
  22. * `f` can return `Promise.<unknown>` or `<unknown>`
  23. *
  24. * Example:
  25. *
  26. * ```
  27. * (async () => {
  28. * let x = await async.map(
  29. * array.range(10),
  30. * x => async.timeout(() => 4*x, 100*x)
  31. * )
  32. * console.log(x)
  33. * })()
  34. * ```
  35. *
  36. * @async
  37. * @function map
  38. * @param {Array} arr
  39. * @param {Function} f
  40. * @returns {Promise.<Array>}
  41. */
  42. export const map = curry((arr, f) => {
  43. const results = [];
  44. let i = 0;
  45. return new Promise((resolve, reject) => {
  46. const progress = (r) => {
  47. results.push(r);
  48. i = inc(i);
  49. if (i < arr.length) {
  50. Promise
  51. .resolve(f.call(arr, arr[i], i))
  52. .then(progress).catch(reject);
  53. } else resolve(results);
  54. };
  55. if (isArray(arr) && isFunction(f)) {
  56. if (arr.length > 0) {
  57. Promise
  58. .resolve(f.call(arr, head(arr), 0))
  59. .then(progress).catch(reject);
  60. } else resolve(results);
  61. } else throw new TypeError(
  62. "async.map() expected array and function, " +
  63. `got ${btquote(arr)} and ${btquote(f)}`,
  64. );
  65. });
  66. });
  67. /**
  68. * Asynchronous version of standard `Array.prototype.map` function.
  69. *
  70. * *Implementation that does paralell execution*.
  71. *
  72. * - `arr` - array to operate on
  73. * - `f` - async or sync function with signature:
  74. * - `this` - bound to `arr`
  75. * - `element` - currently processed element
  76. * - `index` - current index
  77. *
  78. * `f` can return `Promise.<unknown>` or `<unknown>`
  79. *
  80. * Example:
  81. *
  82. * ```
  83. * (async () => {
  84. * let x = await async.parMap(
  85. * array.range(10),
  86. * x => async.timeout(() => 4*x, 100*x)
  87. * )
  88. * console.log(x)
  89. * })()
  90. * ```
  91. *
  92. * @async
  93. * @function parMap
  94. * @param {Array} arr
  95. * @param {Function} f
  96. * @returns {Promise.<Array>}
  97. */
  98. export const parMap = curry((arr, f) =>
  99. Promise.all(arr.map((el, i) => Promise.resolve(f.call(arr, el, i)))),
  100. );
  101. /**
  102. * Asynchronous version of standard `Array.prototype.reduce` function.
  103. *
  104. * - `arr` - array to operate on
  105. * - `f` - async or sync function with signature:
  106. * - `this` - bound to `arr`
  107. * - `acc` - accumulates the `f`'s return values; it is
  108. * the accumulated value previously returned
  109. * in the last invocation of `f`, or `initAcc`, if supplied.
  110. * - `element` - currently processed element
  111. * - `index` - current index
  112. * - `initAcc` - value to use as the first argument to the first call
  113. * of the `f`. If no initial value is supplied, the first element
  114. * in the array will be used.
  115. *
  116. * `f` can return `Promise.<unknown>` or `<unknown>`
  117. *
  118. * Example:
  119. *
  120. * ```
  121. * (async () => {
  122. * let x = await async.reduce(
  123. * array.range(10),
  124. * (acc, x) => async.timeout(() => acc+x, 100*x),
  125. * 0
  126. * )
  127. * console.log(x)
  128. * })()
  129. * ```
  130. *
  131. * @async
  132. * @function reduce
  133. * @param {Array} arr
  134. * @param {Function} f
  135. * @param {unknown} [initAcc]
  136. * @returns {Promise.<unknown>}
  137. */
  138. export const reduce = curry((arr, f, initAcc) => {
  139. let i = 0;
  140. return new Promise((resolve, reject) => {
  141. const progress = r => {
  142. i = inc(i);
  143. if (i < arr.length) {
  144. Promise
  145. .resolve(f.call(arr, r, arr[i], i))
  146. .then(progress).catch(reject);
  147. } else resolve(r);
  148. };
  149. if (isArray(arr) && isFunction(f)) {
  150. if (arr.length > 0) {
  151. Promise
  152. .resolve(f.call(arr, initAcc ?? head(arr), head(arr), 0))
  153. .then(progress).catch(reject);
  154. } else resolve(initAcc);
  155. } else throw new TypeError(
  156. "async.reduce() expected array and function, " +
  157. `got ${btquote(arr)} and ${btquote(f)}`,
  158. );
  159. });
  160. });