Skip to content

Instantly share code, notes, and snippets.

View sherpal's full-sized avatar

Antoine Doeraene sherpal

  • Mibex Software
  • Belgium
View GitHub Profile
@sherpal
sherpal / StarsRating.scala
Created October 29, 2023 14:21
Example of defining "facade" for an (imaginary) web component
object StarsRating {
@js.native
trait RawElement extends js.Object {
def value: Int = js.native
}
type Ref = dom.html.Element with RawElement // 1
private val tag: HtmlTag[Ref] = htmlTag("stars-rating") // 2
setTimeout(() => {
completionState.cancel();
}, 1500);
const completionState = zioService.foreachParN(
[...new Array(30).keys()],
program,
2,
3
);
completionState.progress.forEach((p) => console.log("progress", p));
completionState.result.forEach((r) => console.log("result", r));
const program = (j: number) =>
of(j).pipe(
delay(1000),
tap((j) => {
if (Math.random() > 0.9) {
console.warn(`boom ${j}`);
throw new Error(`${j} kaboom`);
}
}),
tap((e) => console.log(e))
@JSExport
def foreachParN[T, U](ts: js.Array[T],
program: js.Function1[T, Observable[U]],
retries: Int = 2,
parallelism: Int = 3): CompletionState[U] = {
val out = new AsyncSubject[js.Array[U]] // observable for final output
val progressOut = new Subject[Int] // subject to receive progress pings
def closeWithError(err: js.Error): Unit = { // closing output with error
if (!out.closed) {
def execution[T, U](parallelism: Int, retries: Int)(ts: Iterable[T])(
program: T => ZIO[Any, js.Error, U],
nextProgress: ZIO[Any, Nothing, Unit],
complete: List[U] => ZIO[Any, Nothing, Unit],
fail: js.Error => ZIO[Any, Nothing, Unit]
) = {
val policy = retryPolicy(retries)
ZIO
.foreachParN(parallelism)(ts)(program(_).retry(policy) <* nextProgress)
.flatMap(complete)
def retryPolicy(numberOfRetries: Int) =
Schedule.exponential(1.second).jittered && Schedule.recurs(numberOfRetries)
def zioFromRxObservable[U](obs: Observable[U]): ZIO[Any, js.Error, U] =
ZIO.effectAsync { callback =>
obs.subscribe(
(u: U) => callback(UIO(u)), // observable succeeds with a U
(err: js.Any) => // observable fails, probably with a js.Error but we can't know for sure
callback(err match {
case err: js.Error => ZIO.fail(err)
case _ => ZIO.fail(new js.Error(err.toString))
})
)
@JSExportTopLevel("TaskCancelledError")
final class TaskCancelledError extends js.Error("Cancelled by user")
@JSExportAll
case class CompletionState[U](
progress: Observable[Double],
result: Observable[js.Array[U]],
cancel: js.Function0[Unit]
)
def foreachParN[T, U](
ts: js.Array[T],
const users = [
new User("id1", "Alice", new Date("1990-01-05"), new Date("2020-07-15")),
new User("id2", "Bob", undefined, new Date("2020-07-14")),
new User("id3", "Charlie", new Date("1990-07-04"), new Date("2020-06-14")),
new User("id4", "Dean", new Date("1991-09-18"), new Date("2020-07-16")),
];
userService.usersByDateOfBirth(users); // returns [[1990, 2], [1991, 1]]