Skip to content

Commit 0f896c5

Browse files
committed
Implement next(isolation:) in AsyncAdjacentPairsSequence
The next(isolation:) method allows the iterator to inherit the caller's isolation context, avoiding unnecessary isolation hops.
1 parent 6c050d5 commit 0f896c5

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

Sources/AsyncAlgorithms/AsyncAdjacentPairsSequence.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ public struct AsyncAdjacentPairsSequence<Base: AsyncSequence>: AsyncSequence {
6565
self.base = base
6666
}
6767

68+
@available(AsyncAlgorithms 1.1, *)
69+
mutating public func next(isolation actor: isolated (any Actor)?) async throws(Base.Failure) -> (Base.Element, Base.Element)? {
70+
if previousElement == nil {
71+
previousElement = try await base.next(isolation: actor)
72+
}
73+
74+
guard let previous = previousElement, let next = try await base.next(isolation: actor) else {
75+
return nil
76+
}
77+
78+
previousElement = next
79+
return (previous, next)
80+
}
81+
6882
@inlinable
6983
public mutating func next() async rethrows -> (Base.Element, Base.Element)? {
7084
if previousElement == nil {

Tests/AsyncAlgorithmsTests/TestAdjacentPairs.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import XCTest
1313
import AsyncAlgorithms
14+
import Observation
1415

1516
final class TestAdjacentPairs: XCTestCase {
1617
func test_adjacentPairs_produces_tuples_of_adjacent_values_of_original_element() async {
@@ -95,4 +96,29 @@ final class TestAdjacentPairs: XCTestCase {
9596
task.cancel()
9697
await fulfillment(of: [finished], timeout: 1.0)
9798
}
99+
100+
@available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0, *)
101+
@MainActor func test_adjacentPairs_respects_immediate() async {
102+
let testObservable = TestObservable()
103+
let observations = Observations { testObservable.prop }
104+
105+
let iterated = expectation(description: "iterates once")
106+
107+
// with `Task.immediate`, the first element in the adjacent pair should be populated immediately
108+
let t = Task.immediate {
109+
for await (previous, current) in observations.adjacentPairs() {
110+
XCTAssertEqual(previous, 1)
111+
XCTAssertEqual(current, 2)
112+
iterated.fulfill()
113+
}
114+
}
115+
testObservable.prop = 2
116+
await fulfillment(of: [iterated], timeout: 1.0)
117+
t.cancel()
118+
}
119+
}
120+
121+
@available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0, visionOS 26.0, *)
122+
@MainActor @Observable private final class TestObservable {
123+
var prop = 1
98124
}

0 commit comments

Comments
 (0)