RAN012 Reactive read after the first await of an async memo
warning RAN012: userId.Value is read after the first let!/do! of this async memo; a read after an await that suspends is untracked, and a change to userId starts no flight.
Cause. An async memo tracks the reads its body makes while it runs synchronously, up to the first await that suspends. A read in a continuation is untracked, so a change to that source starts no flight. A let! on a task that has already completed does not suspend; the rule reports it too. The rule follows an await in a bind's continuation and after an if, loop or try whose body awaits, and a read anywhere in a loop whose body awaits runs after the previous iteration's await. An await counts when it belongs to the memo's own computation. A read in the body of a memo, effect, boundary or async value created after the await, or under untrack, is tracked by that body and is not reported; a read in createRoot's body, createEffectOn's act or an onCleanup callback is.
Fix. Read the source before the first await and bind it to a local.
Triggers
module Example
open System.Threading.Tasks
open Ranvier
let profile (userId: Signal<int>) =
createAsync (fun _ _ ->
task {
do! Task.Yield ()
return userId.Value
})
Fixed
module Example
open System.Threading.Tasks
open Ranvier
let profile (userId: Signal<int>) =
createAsync (fun _ _ ->
task {
let id = userId.Value
do! Task.Yield ()
return id
})
Suppression. // fsharpanalyzer: ignore-line RAN012, with the reason on the line before.