Swift Academy · Lekcja

Łączenie starszych callbacków i Combine z async/await

Opakowuj completion handlery za pomocą withCheckedContinuation / withCheckedThrowingContinuation i łącz publisherów Combine z AsyncSequence , aby korzystać z prostych pętli for await .

Lekcja 2 z 38 kroki

Łączenie starszych callbacków i Combine z async/await to bezpłatna lekcja Swift Academy na CoddyKit. To lekcja 2 z 3. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Swift Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Swift Academy zawiera 3 lekcji w sumie.

Po co tworzyć most?

Cel: używać kontynuacji do opakowywania wywołań zwrotnych i traktować strumienie Combine jako AsyncSequence.

  • Kontynuacje (niezgłaszające błędów / zgłaszające błędy)
  • Przykład mostkowania URLSession
  • Combine → konsumpcja za pomocą for await

Kontynuacja niezgłaszająca błędów

Należy używać withCheckedContinuation, gdy starsze API nie może zakończyć się błędem. Należy wywołać resume dokładnie raz.

// Old-style API
func fetchName(completion: @escaping (String) -> Void) {
    // simulate async
    Task { try? await Task.sleep(nanoseconds: 80_000_000); completion("Ana") }
}

// Async wrapper using continuation (non-throwing)
func fetchName() async -> String {
    await withCheckedContinuation { (cont: CheckedContinuation<String, Never>) in
        fetchName { value in cont.resume(returning: value) }
    }
}

Task {
    let name = await fetchName()
    print("name:", name) // "Ana"
}

Kontynuacja zgłaszająca błędy

Należy używać withCheckedThrowingContinuation, aby przekazywać błędy starszego API za pomocą throw. Należy zapewnić dokładnie jedną ścieżkę wywołania resume.

enum NetError: Error { case offline; case badStatus }

func loadNumber(completion: @escaping (Result<Int, Error>) -> Void) {
    Task {
        try? await Task.sleep(nanoseconds: 80_000_000)
        completion(.failure(NetError.offline))
    }
}

func loadNumber() async throws -> Int {
    try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Int, Error>) in
        loadNumber { result in
            switch result {
            case .success(let v): cont.resume(returning: v)
            case .failure(let e): cont.resume(throwing: e)
            }
        }
    }
}

Task {
    do {
        let n = try await loadNumber()
        print(n)
    } catch {
        print("error:", error) // offline
    }
}

Mostkowanie URLSession

Należy opakować interfejsy API z funkcją completion handler (np. URLSession), aby uzyskać przejrzystą funkcję async throws. Należy pamiętać o wywołaniu resume dokładnie raz.

import Foundation

func getText(from url: URL) async throws -> String {
    try await withCheckedThrowingContinuation { (cont: CheckedContinuation<String, Error>) in
        let task = URLSession.shared.dataTask(with: url) { data, resp, err in
            if let err = err { cont.resume(throwing: err); return }
            guard let data = data, let text = String(data: data, encoding: .utf8) else {
                cont.resume(throwing: URLError(.badServerResponse)); return
            }
            cont.resume(returning: text)
        }
        task.resume()
    }
}

// Demo (works in environments with network access)
let demoURL = URL(string: "https://example.com")!
Task {
    do { let body = try await getText(from: demoURL); print(body.prefix(15)) }
    catch { print("network error:", error) }
}

Combine jako AsyncSequence

Wiele publisherów Combine udostępnia .values, co pozwala używać for await do iterowania po emitowanych wartościach bez tworzenia własnych mostów.

import Combine
import Foundation

let subject = PassthroughSubject<Int, Never>()

// Consume Combine as AsyncSequence:
Task {
    var produced: [Int] = []
    for await value in subject.values {          // Publisher.values -> AsyncSequence
        produced.append(value)
        if produced.count == 3 { break }         // stop after 3 values
    }
    print("collected:", produced)                // e.g., [1,2,3]
}

// Publish a few values
var cancellables = Set<AnyCancellable>()
Timer.publish(every: 0.05, on: .main, in: .common)
    .autoconnect()
    .prefix(3)
    .scan(0) { acc, _ in acc + 1 }
    .sink { subject.send($0) }
    .store(in: &cancellables)

Publisher → AsyncStream

Jeśli .values jest niedostępne, należy utworzyć niewielki most za pomocą AsyncStream. Należy pamiętać o anulowaniu subskrypcji po zakończeniu pracy.

import Combine

// Bridge ANY Publisher to AsyncSequence using AsyncStream (simple version)
extension Publisher {
    func asAsyncStream() -> AsyncStream<Output> {
        AsyncStream { continuation in
            let cancel = self.sink(
                receiveCompletion: { _ in continuation.finish() },
                receiveValue: { value in continuation.yield(value) }
            )
            continuation.onTermination = { _ in cancel.cancel() }
        }
    }
}

// Demo:
let pub = [10, 20, 30].publisher
Task {
    var sum = 0
    for await v in pub.asAsyncStream() { sum += v }
    print("sum:", sum) // 60
}

Użycie kontynuacji zgłaszającej błędy

Szybkie sprawdzenie: Jaki jest najlepszy sposób na zmostkowanie wywołania zwrotnego, które może zakończyć się błędem, z kodem async?

Podsumowanie

Podsumowanie: Należy opakowywać starsze wywołania zwrotne za pomocą kontynuacji (zgłaszających błędy lub nie), a elementy Combine konsumować przez .values albo niewielki most AsyncStream. Wznowienie należy wykonywać tylko raz, a subskrypcje anulować po zakończeniu pracy.

Bezpłatny start

Ucz się Swift dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
122
Lekcje
409

Często zadawane pytania

Czy lekcja „Łączenie starszych callbacków i Combine z async/await” jest bezpłatna?

Tak — pełny tekst „Łączenie starszych callbacków i Combine z async/await” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Swift Academy, przejdź na CoddyKit PRO. Kurs Swift Academy zawiera 3 lekcji w sumie.

Co nauczysz się w „Łączenie starszych callbacków i Combine z async/await”?

Opakowuj completion handlery za pomocą withCheckedContinuation / withCheckedThrowingContinuation i łącz publisherów Combine z AsyncSequence , aby korzystać z prostych pętli for await . Ćwiczysz Swift Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Swift Academy?

Nie wymagamy żadnego doświadczenia. Swift Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 3.

Ile czasu zajmuje lekcja „Łączenie starszych callbacków i Combine z async/await”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Swift Academy?

Tak. Każda lekcja Swift Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Producent/konsument, potoki i limity czasu
  2. Łączenie starszych callbacków i Combine z async/await
  3. Testowanie kodu asynchronicznego (XCTest)
← Powrót do Swift Academy