- Added
AsyncSeq.mapAsyncParallelThrottled— ordered, bounded-concurrency parallel map. LikemapAsyncParallelbut limits the number of in-flight operations toparallelism, preventing unbounded resource use on large or infinite sequences.
- CI: Upgrade Fable from 4.25.0 to 5.0.0-rc.7 and .NET SDK from 8.0.19 to 10.0.100 to fix a CI hang where the Fable build step ran for 6+ hours with .NET 10. Fable 5 + .NET 10 compiles in ~20 seconds.
- CI: Conditionally disable SourceLink for Fable builds to fix a compilation error where AssemblyInfo.fs was being embedded by SourceLink and passed to the Fable compiler.
- Tests: Update 4 Fable tests that used
rejects.toThrow()to useAsync.Catchwith aChoice2Of2pattern match instead, since Fable 5'sfailwiththrows a customExceptiontype that is not a JavaScriptErrorinstance. - Added
AsyncSeq.ofList— creates an async sequence from an F# list with an optimised direct-enumerator implementation (avoidsIEnumerator<T>boxing). - Added
AsyncSeq.ofArray— creates an async sequence from an array with an optimised index-based enumerator (avoidsIEnumerator<T>boxing). - Added
AsyncSeq.cycle— infinitely cycles through all elements of a source async sequence; returns empty if the source is empty.
- Tests: Added tests for
mapiAsync,tryPickAsync,pickAsync, andgroupByAsync— these four async functions previously had no test coverage. - Performance: Modernised
singleton,collectSeq,takeWhileInclusive, andtakeWhileInclusiveAsyncto usemutablelocal variables instead ofrefcells (!/:=operators). Eliminates heap-allocatedRef<T>objects in these enumerators, reducing GC pressure consistent with the improvements in 4.11.0. - Added
AsyncSeq.tryFindBack— returns the last element for which the predicate returns true, orNoneif no match. MirrorsArray.tryFindBack/List.tryFindBack. - Added
AsyncSeq.tryFindBackAsync— async-predicate variant oftryFindBack. - Added
AsyncSeq.findBack— returns the last element for which the predicate returns true; raisesKeyNotFoundExceptionif no match. MirrorsArray.findBack/List.findBack. - Added
AsyncSeq.findBackAsync— async-predicate variant offindBack. - Added
AsyncSeq.sortAsync— asynchronous variant ofsortreturningAsync<'T[]>, avoidingAsync.RunSynchronouslyin async workflows. - Added
AsyncSeq.sortByAsync— asynchronous variant ofsortByreturningAsync<'T[]>. - Added
AsyncSeq.sortDescendingAsync— asynchronous variant ofsortDescendingreturningAsync<'T[]>. - Added
AsyncSeq.sortByDescendingAsync— asynchronous variant ofsortByDescendingreturningAsync<'T[]>. - Added
AsyncSeq.sortWithAsync— asynchronous variant ofsortWithreturningAsync<'T[]>. - Updated
fsdocs-toolto 22.0.0-alpha.3 (FSharp.Formatting 22.0.0-alpha.3) and updated SDK to .NET 10.0 (issue #300).
- Code/Performance: Modernised ~30 API functions to use
mutablelocal variables instead ofrefcells (!/:=operators). Affected:tryLast,tryFirst,tryItem,compareWithAsync,reduceAsync,scanAsync,pairwise,windowed,pickAsync,tryPickAsync,tryFindIndex,tryFindIndexAsync,threadStateAsync,zipWithAsync,zipWithAsyncParallel,zipWithAsync3,allPairs,takeWhileAsync,takeUntilSignal,skipWhileAsync,skipWhileInclusiveAsync,skipUntilSignal,tryTail,splitAt,toArrayAsync,concatSeq,interleaveChoice,chunkBySize,chunkByAsync,mergeChoiceEnum,distinctUntilChangedWithAsync,emitEnumerator,removeAt,updateAt,insertAt. This eliminates heap-allocatedref-cell objects for these variables, reducing GC pressure in hot paths, and modernises the code style to idiomatic F#. - Performance:
mapiAsync— replacedasyncSeq-builder +collectimplementation with a direct optimised enumerator (OptimizedMapiAsyncEnumerator), eliminatingcollectoverhead and bringing per-element cost in line withmapAsync. Benchmarks added inAsyncSeqMapiBenchmarks. - Design parity with FSharp.Control.TaskSeq (#277, batch 2):
- Added
AsyncSeq.tryTail— returnsNoneif the sequence is empty; otherwise returnsSomeof the tail. Safe counterpart totail. MirrorsTaskSeq.tryTail. - Added
AsyncSeq.where/AsyncSeq.whereAsync— aliases forfilter/filterAsync, mirroring the naming convention inTaskSeqand F# 8 collection expressions. - Added
AsyncSeq.lengthBy/AsyncSeq.lengthByAsync— counts elements satisfying a predicate. MirrorsTaskSeq.lengthBy/TaskSeq.lengthByAsync. - Added
AsyncSeq.compareWith/AsyncSeq.compareWithAsync— lexicographically compares two async sequences using a comparison function. MirrorsTaskSeq.compareWith/TaskSeq.compareWithAsync. - Added
AsyncSeq.takeWhileInclusiveAsync— async variant of the existingtakeWhileInclusive. MirrorsTaskSeq.takeWhileInclusiveAsync. - Added
AsyncSeq.skipWhileInclusive/AsyncSeq.skipWhileInclusiveAsync— skips elements while predicate holds and also skips the first non-matching boundary element. MirrorsTaskSeq.skipWhileInclusive/TaskSeq.skipWhileInclusiveAsync. - Added
AsyncSeq.appendSeq— appends a synchronousseq<'T>after an async sequence. MirrorsTaskSeq.appendSeq. - Added
AsyncSeq.prependSeq— prepends a synchronousseq<'T>before an async sequence. MirrorsTaskSeq.prependSeq. - Added
AsyncSeq.delay— defers sequence creation to enumeration time by calling a factory function each timeGetAsyncEnumeratoris called. MirrorsTaskSeq.delay. - Added
AsyncSeq.collectAsync— likecollectbut the mapping function is asynchronous ('T -> Async<AsyncSeq<'U>>). MirrorsTaskSeq.collectAsync. - Added
AsyncSeq.partition/AsyncSeq.partitionAsync— splits a sequence into two arrays using a (optionally async) predicate; the first array contains matching elements, the second non-matching. MirrorsTaskSeq.partition/TaskSeq.partitionAsync.
- Added
- Tests: added 14 new unit tests covering previously untested functions —
AsyncSeq.indexed,AsyncSeq.iteriAsync,AsyncSeq.tryLast,AsyncSeq.replicateUntilNoneAsync, andAsyncSeq.reduceAsync(empty-sequence edge case).
- Added
AsyncSeq.withCancellation— returns a newAsyncSeqthat passes the givenCancellationTokentoGetAsyncEnumerator, overriding whatever token would otherwise be supplied. MirrorsTaskSeq.withCancellationand is useful when consuming sequences from libraries (e.g. Entity Framework) that accept a cancellation token throughGetAsyncEnumerator. Part of ongoing design-parity work with FSharp.Control.TaskSeq (see #277).
- Performance:
filterAsync— replacedasyncSeq-builder implementation with a direct optimised enumerator, reducing allocation and generator overhead. - Performance:
chooseAsync— fallback (non-AsyncSeqOp) path now uses a direct optimised enumerator instead of theasyncSeqbuilder. - Performance:
foldAsync— fallback (non-AsyncSeqOp) path now uses a direct loop instead of composingscanAsync+lastOrDefault, avoiding intermediate sequence allocations. - Performance:
take— replacedasyncSeq-builder implementation with a direct optimised enumerator (OptimizedTakeEnumerator), eliminating generator-machinery overhead for this common slicing operation. - Performance:
skip— replacedasyncSeq-builder implementation with a direct optimised enumerator (OptimizedSkipEnumerator), eliminating generator-machinery overhead for this common slicing operation. - Benchmarks: added
AsyncSeqFilterChooseFoldBenchmarks,AsyncSeqPipelineBenchmarks, andAsyncSeqSliceBenchmarksbenchmark classes.
- Added
AsyncSeq.mapFoldAsync— maps each element using an asynchronous folder that also threads an accumulator state, returning both the array of results and the final state; mirrorsSeq.mapFold. - Added
AsyncSeq.mapFold— synchronous variant ofAsyncSeq.mapFoldAsync, mirroringSeq.mapFold. - Added
AsyncSeq.allPairs— returns an async sequence of all pairs from two input sequences (cartesian product); the second source is fully buffered before iteration, mirroringSeq.allPairs. - Added
AsyncSeq.rev— returns a new async sequence with all elements in reverse order; the entire source sequence is buffered before yielding, mirroringSeq.rev.
- Added
AsyncSeq.splitAt— splits a sequence at the given index, returning the firstcountelements as an array and the remaining elements as a newAsyncSeq. MirrorsSeq.splitAt. The source is enumerated once. - Added
AsyncSeq.removeAt— returns a new sequence with the element at the specified index removed, mirroringSeq.removeAt. - Added
AsyncSeq.updateAt— returns a new sequence with the element at the specified index replaced by a given value, mirroringSeq.updateAt. - Added
AsyncSeq.insertAt— returns a new sequence with a value inserted before the element at the specified index (or appended if the index equals the sequence length), mirroringSeq.insertAt.
- Added
AsyncSeq.isEmpty— returnstrueif the sequence contains no elements; short-circuits after the first element, mirroringSeq.isEmpty. - Added
AsyncSeq.tryHead— returns the first element asoption, orNoneif the sequence is empty, mirroringSeq.tryHead(equivalent to the existingAsyncSeq.tryFirst). - Added
AsyncSeq.except— returns a new sequence excluding all elements present in a given collection, mirroringSeq.except. - Added
AsyncSeq.findIndex— returns the index of the first element satisfying a predicate; raisesKeyNotFoundExceptionif no match, mirroringSeq.findIndex. - Added
AsyncSeq.tryFindIndex— returns the index of the first element satisfying a predicate asoption, orNoneif not found, mirroringSeq.tryFindIndex. - Added
AsyncSeq.findIndexAsync— async-predicate variant ofAsyncSeq.findIndex; raisesKeyNotFoundExceptionif no match. - Added
AsyncSeq.tryFindIndexAsync— async-predicate variant ofAsyncSeq.tryFindIndex; returnsoption. - Added
AsyncSeq.sortWith— sorts the sequence using a custom comparison function, returning an array, mirroringSeq.sortWith.
- Added
AsyncSeq.last— returns the last element of the sequence; raisesInvalidOperationExceptionif empty, mirroringSeq.last. - Added
AsyncSeq.item— returns the element at the specified index; raisesArgumentExceptionif out of bounds, mirroringSeq.item. - Added
AsyncSeq.tryItem— returns the element at the specified index asoption, orNoneif the index is out of bounds, mirroringSeq.tryItem.
- Added
AsyncSeq.findAsync— async-predicate variant ofAsyncSeq.find; raisesKeyNotFoundExceptionif no match, mirroringSeq.find. - Added
AsyncSeq.existsAsync— async-predicate variant ofAsyncSeq.exists, mirroringSeq.exists. - Added
AsyncSeq.forallAsync— async-predicate variant ofAsyncSeq.forall, mirroringSeq.forall.
- Added
AsyncSeq.head— returns the first element of the sequence; raisesInvalidOperationExceptionif empty, mirroringSeq.head. - Added
AsyncSeq.iteri— iterates the sequence calling a synchronous action with each element's index, mirroringSeq.iteri. - Added
AsyncSeq.find— returns the first element satisfying a predicate; raisesKeyNotFoundExceptionif no match, mirroringSeq.find. - Added
AsyncSeq.tryFindAsync— async-predicate variant ofAsyncSeq.tryFind, returning the first matching element asoption. - Added
AsyncSeq.tail— returns the sequence without its first element, mirroringSeq.tail.
- Added
AsyncSeq.zip3,AsyncSeq.zipWith3, andAsyncSeq.zipWithAsync3— combinators for zipping three async sequences, mirroringSeq.zip3(PR #254).
async sequence (PR #241).
- Added
AsyncSeq.reduceandAsyncSeq.reduceAsync— folds without a seed value, mirroringSeq.reduce(PR #242). - Added
AsyncSeq.sumBy,AsyncSeq.sumByAsync,AsyncSeq.average,AsyncSeq.averageBy, andAsyncSeq.averageByAsync— numeric aggregation combinators mirroring the correspondingSeqmodule functions (PR #245). - Added
AsyncSeq.min,AsyncSeq.max,AsyncSeq.minBy,AsyncSeq.maxBy,AsyncSeq.minByAsync, andAsyncSeq.maxByAsync— min/max aggregation combinators mirroringSeq.min/Seq.max/Seq.minBy/Seq.maxBy(PR #243). - Added
AsyncSeq.distinct,AsyncSeq.distinctBy,AsyncSeq.distinctByAsync,AsyncSeq.countBy,AsyncSeq.countByAsync,AsyncSeq.exactlyOne, andAsyncSeq.tryExactlyOne— set-membership and cardinality combinators mirroring the correspondingSeqmodule functions (PR #249).
- Breaking:
AsyncSeq<'T>is nowSystem.Collections.Generic.IAsyncEnumerable<'T>(the BCL type).ofAsyncEnumandtoAsyncEnumare now identity functions and marked[<Obsolete>]. Code that directly calls.GetEnumerator()/.MoveNext()must switch to.GetAsyncEnumerator(ct)/.MoveNextAsync()(#230, PR #231). - Added
YieldFromoverload forseq<'T>inasyncSeqcomputation expression —yield! itemsnow works whenitemsis aseq<'T>(#123, PR #236). - Added
[<CompilerMessage>]warning togroupByandgroupByAsyncto alert users that results must be consumed with a parallel combinator to avoid deadlock (#125, PR #235). - Added
AsyncSeq.mapAsyncUnorderedParallelThrottledfor throttled unordered parallel mapping (#31, PR #225). - Fixed
ofAsyncEnumdeadlock on single-threaded runtimes such as Blazor WASM (#152, PR #229). - Updated
PackageLicenseExpression(removed deprecatedPackageLicenseUrl) and updatedMicrosoft.Bcl.AsyncInterfacesto 10.0.3 (#168, PR #228).
- Quick summary of changes:
- Performance improvements: optimized
iterAsyncanditeriAsync; optimizedcollect,mapAsyncandunfoldAsync; fixed append memory leak (Issue #35). - Added
mapAsyncUnorderedParallelfor improved parallel performance. - Added
AsyncSeq.chunkByandAsyncSeq.chunkByAsyncfor grouping consecutive elements by key (#156, PR #222). AsyncSeq.mergeAllnow acceptsseq<AsyncSeq<'T>>instead oflist<AsyncSeq<'T>>for broader compatibility (#165, PR #221).- Set up BenchmarkDotNet for systematic benchmarking; performance benchmarks show measurable improvements (addresses Round 1 & 2 of #190).
- Build/CI updates: configuration and build-step updates.
- Performance improvements: optimized
- Release latest
- Sorting functions #126
- Update publishing
- Restore netstandard2.0 (and thereby .NET Framework 4.6.1+) compatibility
- Update build env versions to current recommendations
- Adjust to ensure all tests pass and eliminate warnings in vscode (ionide) and visual studio
- Include .fsi files into Fable package path #118
- Move to only netstandard 2.1
- Adding ofIQueryable #112
- Adding .NET IAsyncEnumerable conversion functions (ofAsyncEnum and toAsyncEnum) #96
- Rename toList and toArray to toListSynchronously and toArraySynchronously
- Add ofSeqAsync and concat
- Improve parallelism of AsyncSeq.cache
- Fix packaging issues
- Reference FSharp.Core 4.3 for nestandard builds
- Fix packaging issues
- Reference FSharp.Core 4.3 for nestandard builds
- Fix packaging issues
- Reference FSharp.Core 4.3 for nestandard builds
- Target .NET Standard
- Target .NET Standard
- AsyncSeq.mergeAll min-max fairness
- Improve performance of internal Async.chooseTasks function which improves performance of AsyncSeq.bufferByCountAndTime, etc (#73)
- Fix previous package deployment
- NEW: AsyncSeq.bufferByTime
- BUG: Fixed head of line blocking in AsyncSeq.mapAsyncParallel
- NEW: AsyncSeq.takeWhileInclusive
- NEW: AsyncSeq.replicateUntilNoneAsync
- NEW: AsyncSeq.iterAsyncParallel
- NEW: AsyncSeq.iterAsyncParallelThrottled
- BUG: Fixed exception propagation in AsyncSeq.mapAsyncParallel
- Fix bug #63 in AsyncSeq.unfold >> AsyncSeq.choose
- Fixed bug in AsyncSeq.cache when used by interleaved consumers.
- AsyncSeq.zipWithAsyncParallel (and variants)
- Improved asyncSeq workflow performance via bindAsync generator (@pragmatrix)
- Much improved append performance.
- Direct implementation of unfoldAsync as IAsyncEnumerable, with chooseAsync, mapAsync and foldAsync overrides
- Add portable7 profile
- Fix bug in Async.cache #33
- Fix leak in AsyncSeq.append and other derived generators
- Add AsyncSeq.sum, length, contains, exists, forall, tryPick, tryFind
- Simplify ofObservableBuffered and toBlockingSeq
- Move to IAsyncEnumerable model to support try/finally and try/with
- Rename replicate to replicateInfinite
- Rename toList to toListAsync
- Rename toArray to toArrayAsync
- Rename zipWithIndexAsync to mapiAsync
- Rename interleave to interleaveChoice
- Add interleave
- Add mergeChoice
- Fix performance of mergeAll
- Add init, initInfinite
- Add initAsync, initInfiniteAsync, replicateInfinite
- Add RequireQualifiedAccess to AsyncSeq
- Add AsyncSeq.getIterator (unblocks use of AsyncSeq in FSharpx.Async)
- Cancellable AsyncSeq.toBlockingSeq
- Fix AsyncSeq.scanAsync to also return first state
- Add a signature file
- AsyncSeq got extracted as separate project and is now a dependency - fsprojects/FSharpx.Async#24
- Renamed to FSharp.Control.AsyncSeq
- Remove surface area
- Hide Nil/Cons from representation of AsyncSeq
- Added Async.bindChoice, Async.ParallelIgnore, AsyncSeq.zipWithAsync, AsyncSeq.zappAsync, AsyncSeq.threadStateAsync, AsyncSeq.merge, AsyncSeq.traverseOptionAsync, AsyncSeq.traverseChoiceAsync
- Added AsyncSeq.toList, AsyncSeq.toArray, AsyncSeq.bufferByCount, AsyncSeq.unfoldAsync, AsyncSeq.concatSeq, AsyncSeq.interleave
- Copied the AsyncSeq from FSharpx.Async
- BUGFIX: AsyncSeq.skipWhile skips an extra item - #2
- BUGFIX: AsyncSeq.skipWhile skips an extra item - #2
- BUGFIX: AsyncSeq.toBlockingSeq does not hung forever if an exception is thrown and reraise it outside - #21