[SOLVED]How to combine Async.after with another Async function?



I want to send some http requests to a target, but not all at once, I’d like to send a request once a second. My demo would be:

open Core
open Async
open Cohttp
open Cohttp_async

let mock_lst = [111; 222; 333; 666; 234; 982]

let g_log = "log.txt"

let wlog msg :unit Deferred.t =
  Writer.with_file g_log 
    ~f:(fun w -> Writer.write_line w msg; return ())

let seq_send url param : unit Deferred.t =
  let uri = Uri.of_string (url^"?"^param) in
  let%bind _, body = Cohttp_async.Client.get uri in
  let%bind body = Body.to_string body in
  wlog body

let ord_send lst =
  List.iteri lst
    ~f:(fun idx ele ->
        after (sec (float_of_int idx)) >>= fun () ->
        (seq_send "" (string_of_int ele)))

let () =
    (fun () -> ord_send mock_lst)
  |> Command.run

I tried to compile and there’s an error

$ corebuild -pkg async,cohttp,cohttp.async osend.native

Error: This expression has type unit Conduit_async.io
       but an expression was expected of type unit

The error code snippet:

        after (sec (float_of_int idx)) >>= fun () ->
        (seq_send "" (string_of_int ele)))

I tried to debug by reading the type of each function:

# Async.after;;
- : Time.Span.t -> unit Conduit_async.io = <fun>


# Deferred.bind;;
- : 'a Conduit_async.io -> f:('a -> 'b Conduit_async.io) -> 'b Conduit_async.io = <fun>

My code after (sec (float_of_int idx)) has type unit Conduit_async.io which should match the first parameter of Deferred.bind.

The second parameter of Deferred.bind would be f:('a -> 'b Conduit_async.io), my function is fun () -> (seq_send "" (string_of_int ele))) should be matched(unit -> unit Conduit_async.io)

I was wondering what’s going on with my code?


Try using Deferred.List.iter instead of List.iter. The deferred version expects the function to return a value of type unit Deferred.t, instead of simply unit. The default is for the operations in question to be done in sequence, which is what you want in this case.