         with services: Set<BluetoothUUID>,
         filterDuplicates: Bool = true
     ) -> AsyncThrowingStream<ScanData<Peripheral, Advertisement>, Error> {
-        let serviceUUIDs: [CBUUID]? = services.isEmpty ? nil : services.map { CBUUID($0) }
-        let options: [String: Any] = [
-            CBCentralManagerScanOptionAllowDuplicatesKey: NSNumber(value: filterDuplicates == false)
-        ]
-        self.log("Will scan for nearby devices")
         return AsyncThrowingStream(ScanData<Peripheral, Advertisement>.self, bufferingPolicy: .bufferingNewest(100)) {  [weak self] continuation in
             guard let self = self else { return }
             self.async {
-                // cancel old scanning task
-                if let oldContinuation = self.continuation.scan {
-                    oldContinuation.finish(throwing: CancellationError())
-                    self.continuation.scan = nil
-                }
-                // reset cache
-                self.cache = Cache()
-                // start scanning
-                assert(self.continuation.scan == nil)
-                self.continuation.scan = continuation
-                self.continuation.isScanning.yield(true)
-                self.centralManager.scanForPeripherals(withServices: serviceUUIDs, options: options)
+                let operations = Operation.Scan(
+                    services: services,
+                    filterDuplicates: filterDuplicates,
+                    continuation: continuation
+                )
+                self.continuation.scan.push(operations)
             }
         }
     }
     
     public func stopScan() async {
-        self.log("Stopped scanning")
         return await withCheckedContinuation { [weak self] continuation in
             guard let self = self else { return }
             self.async {
-                guard let scanContinuation = self.continuation.scan else {
+                guard let _ = self.continuation.scan.current else {
                     continuation.resume() // not currently scanning
                     return
                 }
-                self.centralManager.stopScan()
-                self.continuation.isScanning.yield(false)
-                self.log("Discovered \(self.cache.peripherals.count) peripherals")
-                scanContinuation.finish(throwing: nil) // end stream
-                continuation.resume()
-                self.continuation.scan = nil
+                self.continuation.scan.pop { operation in
+                    self.centralManager.stopScan()
+                    self.continuation.isScanning.yield(false)
+                    self.log("Discovered \(self.cache.peripherals.count) peripherals")
+                    operation.continuation.finish(throwing: nil) // end stream
+                    continuation.resume()
+                }
             }
         }
     }

            self.restoreIdentifier = restoreIdentifier
        }
        
        internal var optionsDictionary: [String: Any] {
            var options = [String: Any](minimumCapacity: 2)
            if showPowerAlert {
                options[CBCentralManagerOptionShowPowerAlertKey] = showPowerAlert as NSNumber

            // cache value
            self.central.cache.peripherals[peripheral] = corePeripheral
            // yield value to stream
            guard let operation = self.central.continuation.scan.current else {
                assertionFailure("Not currently scanning")
                return
            }
            operation.continuation.yield(scanResult)
        }
        
        #if os(iOS)
