|
1 | 1 | /* eslint-disable @typescript-eslint/no-misused-promises, @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-use-before-define */
|
2 |
| -import { constant, identity, range, Validation } from "../util" |
| 2 | +import { constant, drop, identity, range, take, Validation } from "../util" |
3 | 3 |
|
4 | 4 | export type Reject<E> = (error: E) => void
|
5 | 5 | export type Resolve<S> = (result: S) => void
|
@@ -393,41 +393,7 @@ export const firstSuccess = <E, S>(tasks: Array<Task<E, S>>): Task<E[], S> =>
|
393 | 393 | * @param tasks The tasks to run in parallel.
|
394 | 394 | */
|
395 | 395 | export const all = <E, S>(tasks: Array<Task<E, S>>): Task<E, S[]> =>
|
396 |
| - tasks.length === 0 |
397 |
| - ? of([]) |
398 |
| - : new Task<E, S[]>((reject, resolve) => { |
399 |
| - let isDone = false |
400 |
| - let runningTasks = tasks.length |
401 |
| - |
402 |
| - const results: S[] = [] |
403 |
| - |
404 |
| - return tasks.map((task, i) => |
405 |
| - task.fork( |
406 |
| - (error: E) => { |
407 |
| - if (isDone) { |
408 |
| - return |
409 |
| - } |
410 |
| - |
411 |
| - isDone = true |
412 |
| - |
413 |
| - reject(error) |
414 |
| - }, |
415 |
| - (result: S) => { |
416 |
| - if (isDone) { |
417 |
| - return |
418 |
| - } |
419 |
| - |
420 |
| - runningTasks -= 1 |
421 |
| - |
422 |
| - results[i] = result |
423 |
| - |
424 |
| - if (runningTasks === 0) { |
425 |
| - resolve(results) |
426 |
| - } |
427 |
| - }, |
428 |
| - ), |
429 |
| - ) |
430 |
| - }) |
| 396 | + sequence(tasks, Infinity) |
431 | 397 |
|
432 | 398 | /**
|
433 | 399 | * Given an array of task which return a result, return a new task which returns an array of successful results.
|
@@ -494,11 +460,61 @@ export const zipWith = <E, E2, S, S2, V>(
|
494 | 460 | * Given an array of task which return a result, return a new task which results an array of results.
|
495 | 461 | * @param tasks The tasks to run in sequence.
|
496 | 462 | */
|
497 |
| -export const sequence = <E, S>(tasks: Array<Task<E, S>>): Task<E, S[]> => |
498 |
| - tasks.reduce( |
499 |
| - (sum, task) => chain(list => map(result => [...list, result], task), sum), |
500 |
| - succeed([]) as Task<E, S[]>, |
501 |
| - ) |
| 463 | +export const sequence = <E, S>( |
| 464 | + tasks: Array<Task<E, S>>, |
| 465 | + maxConcurrent = 1, |
| 466 | +): Task<E, S[]> => |
| 467 | + new Task((reject, resolve) => { |
| 468 | + let isDone = false |
| 469 | + |
| 470 | + type TaskPosition = [Task<E, S>, number] |
| 471 | + |
| 472 | + let queue = tasks.map<TaskPosition>((task, i) => [task, i]) |
| 473 | + const inflight = new Set<Task<E, S>>() |
| 474 | + const results: S[] = [] |
| 475 | + |
| 476 | + const enqueue = () => { |
| 477 | + if (isDone) { |
| 478 | + return |
| 479 | + } |
| 480 | + |
| 481 | + if (queue.length <= 0 && inflight.size <= 0) { |
| 482 | + isDone = true |
| 483 | + resolve(results) |
| 484 | + return |
| 485 | + } |
| 486 | + |
| 487 | + const howMany = Math.min(queue.length, maxConcurrent - inflight.size) |
| 488 | + |
| 489 | + const readyTasks = take(howMany, queue) |
| 490 | + queue = drop(howMany, queue) |
| 491 | + |
| 492 | + readyTasks.forEach(([task, i]) => { |
| 493 | + inflight.add(task) |
| 494 | + |
| 495 | + task.fork( |
| 496 | + (error: E) => { |
| 497 | + if (isDone) { |
| 498 | + return |
| 499 | + } |
| 500 | + |
| 501 | + isDone = true |
| 502 | + |
| 503 | + reject(error) |
| 504 | + }, |
| 505 | + (result: S) => { |
| 506 | + results[i] = result |
| 507 | + |
| 508 | + inflight.delete(task) |
| 509 | + |
| 510 | + enqueue() |
| 511 | + }, |
| 512 | + ) |
| 513 | + }) |
| 514 | + } |
| 515 | + |
| 516 | + enqueue() |
| 517 | + }) |
502 | 518 |
|
503 | 519 | /**
|
504 | 520 | * Given a task, swap the error and success values.
|
|
0 commit comments