Create and Await Promises using lwt

Task

Concurrency & Parallelism / Single-Threaded Concurrency / Create and Await Promises

Create promises and run them in parallel, as well as sequentially. Includes a sleep task.

Opam Packages Used

  • lwt Tested with version: 5.7.0 — Used libraries: lwt, lwt.unix

Code

We define shorthands for the Lwt binding operators to make our concurrent code more readable. let* a = <Lwt promise> in b schedules the Lwt promise, waits for its result, then excutes b. In contrast, let*? a = <promise> in b short-circuits on error by using a Result type: execution continues if the result is Ok x, and stops if the result was Error err.

let ( let* ) = Lwt.bind
let ( let*? ) = Lwt_result.bind

Example task with return value to demonstrate concurrent execution.

let task n =
  let* () = Lwt_io.printf "Task %d - first line\n" n in
  let* () = Lwt.pause () in
  let* () = Lwt_io.printf "Task %d - second line\n" n in
  Lwt.return n

Example task that only produces side effects, without a return value.

let task' n =
  let* result = task n in
  Lwt.return_unit

let () =

Run two tasks in parallel and collect the results.

  let run_two_parallel_tasks () =
    Lwt_main.run @@ Lwt.both (task 1) (task 2)
  in
  Printf.printf "\n= Running Two Parallel Tasks =\n";
  let (result1, result2) = run_two_parallel_tasks () in
  Printf.printf "Results: (%s, %s)\n"
    (string_of_int result1)
    (string_of_int result2);

Run tasks in parallel and collect results.

  let parallel_map () =
    Lwt_main.run @@ Lwt_list.map_p task [1; 2; 3]
  in
  Printf.printf "\n= Parallel Map Example =\n";
  let parallel_results = parallel_map () in
  Printf.printf "Results: [%s]\n"
    (String.concat "; "
      (List.map string_of_int parallel_results));

Run tasks sequentially and collect results.

  let sequential_map () =
    Lwt_main.run @@ Lwt_list.map_s task [1; 2; 3]
  in
  Printf.printf "\n= Sequential Map Example =\n";
  let sequential_results = sequential_map () in
  Printf.printf "Results: [%s]\n"
    (String.concat "; "
      (List.map string_of_int sequential_results));

Run tasks in parallel for side effects.

  let parallel_iter () =
    Lwt_main.run @@ Lwt_list.iter_p task' [1; 2; 3]
  in
  Printf.printf "\n= Parallel Iteration Example =\n";
  parallel_iter ();

Run tasks sequentially for side effects.

  let sequential_iter () =
    Lwt_main.run @@ Lwt_list.iter_s task' [1; 2; 3]
  in
  Printf.printf "\n= Sequential Iteration Example =\n";
  sequential_iter ();

  let sleep_task () =
    Lwt_main.run @@ (Lwt_unix.sleep 1.)
  in
  Printf.printf "\n= Sleep Example (1 second) =\n";
  sleep_task ();
  Printf.printf "Finished sleeping\n"

Discussion

Lwt is a scheduler that permits concurrent execution of promises on a single thread. Promise switching is performed at each long I/O (using the Lwt_io module), timing pause (Lwt_unix.sleep), or explicit promise switching (Lwt.pause ()). Promises are typically composed with the Lwt.bind function. More details about the monad bind function is given in the monad section. The let operators are detailed in the operator section. Refer to the Lwt manual.

ppx_let can be used to enable the syntax let%lwt instead of defining let* as a shorthand for `Lwt.bind. This makes it obvious on first glance which monad we're operating in.

Recipe not working? Comments not clear or out of date?

Open an issue or contribute to this recipe!