I kind of know the syntax of asynchronous programming in F#. E.g.
let downloadUrl(url:string) = async {
let req = HttpWebRequest.Create(url)
// Run oper
I think the most important thing to understand about asynchronous workflows is that they are sequential in the same way as ordinary code written in F# (or C#, for that matter) is sequential. You have some let bindings that evaluate in the usual order and some expressions (that may have side-effects). In fact, asynchronous workflows often look more like imperative code.
The second important aspect of asynchronous workflows is that they are non-blocking. This means that you can have operations that are executed in some non-standard way and do not block the thread while executing. (In general, let! in F# computation expressions always signals that there is some non-standard behavior - it may be possibility to fail without producing result in the Maybe monad, or it may be non-blocking execution for asynchronous workflows).
Technically speaking, non-blocking execution is implemented by registering some callback that will be triggered when the operation completes. Relatively simple example is an asynchronous workflow that waits some specified time - this can be implemented using Timer without blocking any threads (Example from chapter 13 of my book, source is available here):
// Primitive that delays the workflow
let Sleep(time) =
// 'FromContinuations' is the basic primitive for creating workflows
Async.FromContinuations(fun (cont, econt, ccont) ->
// This code is called when workflow (this operation) is executed
let tmr = new System.Timers.Timer(time, AutoReset=false)
tmr.Elapsed.Add(fun _ ->
// Run the rest of the computation
cont())
tmr.Start() )
There are also several ways to use F# asynchronous workflows for parallel or concurrent programming, however these are just more sophisticated uses of F# workflows or libraries built on top of them - they take the advantage of non-blocking behavior described earlier.
You can use StartChild to start a workflow in background - the method gives you a running workflow that you can use (using let!) later in the workflow to wait for completion, while you can continue doing other things. This is similar to Tasks in .NET 4.0, but it runs asynchronously, so it is more suitable for I/O operations.
You can use Async.Parallel to create multiple workflows and wait until all of them complete (which is great for data-parallel operations). This is similar to PLINQ, but again, async is better if you do some I/O operations.
Finally, you can use MailboxProcessor which allows you to write concurrent applications using the message-passing style (Erlang style). This is a great alternative to threads for many problems.