.task { } — ano ito, async modifier at pag-load ng data sa View

May-akda: IT Sectr Nai-publish: 2026-06-26 Oras ng pagbabasa: 9 min

.task { } — ay isang modifier sa SwiftUI, na ipinakilala sa iOS 15, na naglulunsad ng asynchronous na operasyon kapag lumitaw ang View at awtomatikong kinakansela ito kapag nawala. Hindi tulad ng .onAppear, na nagsasagawa ng synchronous na code nang walang kakayahang kanselahin, ang .task ay gumagana sa async/await na konteksto at isinasaalang-alang ang lifecycle ng View: kapag nawala ang View, tinatawag ng SwiftUI ang cancel() sa nilikhang Task. Pinipigilan nito ang mga leak ng memorya at pagpapatakbo ng mga operasyon pagkatapos na hindi na kailangang i-update ang View. Ayon sa Apple WWDC Session 10132 — Meet async/await in SwiftUI (2024), ang .task ay ang ginustong paraan ng pag-load ng data sa SwiftUI, dahil ligtas itong gumagana sa Structured Concurrency at awtomatikong namamahala sa buhay ng asynchronous na operasyon.

Mga Pangunahing Punto

  • .task { } — SwiftUI modifier para sa asynchronous na pag-load ng data kapag lumitaw ang View, available mula iOS 15.
  • Awtomatikong pagkansela — kapag nawala ang View, kinakansela ng SwiftUI ang Task, pinipigilan ang mga leak ng memorya.
  • Konteksto ng async/await — sa loob ng .task ay available ang mga async na tawag nang hindi nangangailangan ng DispatchQueue o Combine.
  • .task(id:) — variant na may identifier ay nag-restart ng gawain kapag nagbago ang tinukoy na halaga.
  • Structured Concurrency — sinusuportahan ng .task ang Structured Concurrency at TaskGroup para sa mga parallel na operasyon.

Ano ang .task { } sa SwiftUI

.task { } — ay isang View modifier na lumilikha ng Task sa async na konteksto kapag lumitaw ang View sa screen. Inilulunsad ng SwiftUI ang ibinigay na closure sa background, habang ang pangunahing thread ay nananatiling libre para sa mga UI operation. Kapag nawala ang View, awtomatikong kinakansela ng SwiftUI ang Task sa pamamagitan ng Structured Concurrency mechanism — ginagarantiyahan nito na ang asynchronous na operasyon ay hindi magpapatuloy pagkatapos na ang resulta nito ay hindi na kailangan ng sinuman.

Ayon sa Apple — Swift Programming Language (2025), ginagamit ng .task ang konsepto ng Structured Concurrency, na ipinakilala sa Swift 5.5. Ang bawat .task ay lumilikha ng child task sa loob ng parent task ng View. Kung ang parent task ay nakansela (nawala ang View), lahat ng child task ay awtomatikong nakansela rin. Ito ay radikal na nagpapasimple sa pamamahala ng lifecycle ng mga asynchronous na operasyon kumpara sa manu-manong pag-iimbak ng mga referensya sa DispatchWorkItem o AnyCancellable.

Hindi tulad ng tradisyonal na diskarte na may @State + manu-manong tawag sa .onAppear, ang .task ay hindi nangangailangan ng pag-iimbak ng referensya sa Task para sa susunod na pagkansela. Ginagawa ito ng SwiftUI nang awtomatiko, na nagbabawas sa dami ng boilerplate code at nag-aalis ng panganib na makalimutang kanselahin ang gawain.

swift
struct ArticlesView: View {
    @State var articles: [Article] = []
    @State var error: Error?
    
    var body: some View {
        List(articles) { article in
            Text(article.title)
        }
        .task {
            do {
                articles = await APIClient().fetchArticles()
            } catch {
                self.error = error
            }
        }
    }
}

.task vs .onAppear: mga pangunahing pagkakaiba

Maraming developer ang sanay na mag-load ng data sa .onAppear, ngunit sa pagdating ng async/await at .task, ang diskarteng ito ay luma na. Ang .onAppear ay nagsasagawa ng code nang synchronously — para sa mga asynchronous na operasyon sa loob ng .onAppear, kailangan mong balutin ang tawag sa Task { } at manu-manong mag-imbak ng referensya dito para sa posibleng pagkansela. Ginagawa ito ng .task nang awtomatiko.

Katangian.task { }.onAppear
Async na kontekstoNakapaloob na async/awaitNangangailangan ng Task { } balot
Awtomatikong pagkanselaOo, kapag nawala ang ViewHindi, kailangan manual na ipatupad
Structured ConcurrencySinusuportahanHindi sinusuportahan
Pag-restartKapag nagbago lang ang idTuwing lumilitaw
Rekomendasyon ng AppleGinustong paraanPara sa synchronous na operasyon

.onAppear ay kapaki-pakinabang pa rin para sa mga synchronous na operasyon — halimbawa, pag-log o paunang configuration ng UI. Ngunit para sa asynchronous na pag-load ng data, mga network request, pagtatrabaho sa database o file system, gamitin ang .task. Ito ay mas ligtas at mas malinis mula sa pananaw ng arkitektura.

swift
// ❌ Lumang diskarte: Task sa .onAppear nang walang pagkansela
var loadTask: Task<Void, Never>?
func body() { var body: some View { Text("") }
    .onAppear {
        loadTask = Task { await loadData() }
    }
    .onDisappear { loadTask?.cancel() }

// ✅ Makabagong diskarte: .task ang namamahala sa pagkansela
func body() { var body: some View { Text("") }
    .task { await loadData() }

.task(id:) — pag-restart kapag nagbago ang data

Ang modifier na .task(id:) ay tumatanggap ng karagdagang parameter — isang identifier. Kapag nagbago ang halaga ng identifier, kinakansela ng SwiftUI ang kasalukuyang gawain at naglulunsad ng bago na may bagong identifier. Ito ay perpekto para sa mga screen kung saan ang data ay nakadepende sa napiling parameter — halimbawa, listahan ng mga artikulo ayon sa kategorya o mga detalye ng produkto ayon sa ID.

swift
struct CategoryView: View {
    let categoryId: Int
    @State var items: [Item] = []
    
    var body: some View {
        List(items) { item in
            Text(item.name)
        }
        .task(id: categoryId) {
            await loadItems(for: categoryId)
        }
    }
    
    func loadItems(for id: Int) async {
        do {
            items = await APIClient().fetchItems(categoryId: id)
        } catch {
            // hawakan ang error
        }
    }
}

Kapag nagbago ang categoryId, kinakansela ng SwiftUI ang nakaraang request at naglulunsad ng bago. Ito ay lalong mahalaga sa mabilis na pagpalit ng mga kategorya — ang mga lumang request ay hindi makikipagkumpitensya sa mga bago para sa pag-update ng state. Kung walang .task(id:) kailangan mong manu-manong subaybayan ang mga pagbabago sa pamamagitan ng .onChange at manu-manong pamahalaan ang Task.

Pagkansela ng mga gawain at pagsusuri ng isCancelled

Kahit na ang .task ay awtomatikong kinakansela ang gawain kapag nawala ang View, ang asynchronous na operasyon mismo ay dapat na kooperatibong suriin ang pagkansela. Gumagamit ang Swift ng kooperatibong modelo ng pagkansela — ang Task.cancel() ay hindi puwersahang humihinto sa pagpapatakbo, ngunit nagtatakda lamang ng flag na isCancelled. Ang code sa loob ng gawain ay dapat na pana-panahong suriin ang flag na ito.

swift
struct LoadingView: View {
    @State var progress: Double = 0
    
    var body: some View {
        ProgressView(value: progress)
            .task {
                for i in 0..<100 {
                    // Suriin ang pagkansela
                    try Task.checkCancellation()
                    
                    await Task.sleep(nanoseconds: 50_000_000)
                    progress = Double(i + 1) / 100.0
                }
            }
    }
}

Task.checkCancellation() ay nagtatapon ng CancellationError kung ang gawain ay nakansela. Ito ang pinakasimpleng paraan ng pagsusuri — gumagana sa anumang async na konteksto. Alternatibo — manu-manong pagsusuri ng Task.isCancelled bago ang mga mamahaling operasyon. Para sa URLSession, ang mga network request ay awtomatikong nakansela kapag nakansela ang gawain, dahil sinusuportahan ng URLSession ang Structured Concurrency mula sa simula.

Mga praktikal na halimbawa ng .task

.task ay angkop para sa maraming senaryo: mula sa simpleng pag-load ng JSON hanggang sa kumplikadong parallel na operasyon gamit ang TaskGroup. Tingnan natin ang tatlong tipikal na halimbawa ng paggamit.

Pag-load na may paghawak ng error

swift
struct ProfileView: View {
    @State var profile: Profile?
    @State var isLoading = true
    
    var body: some View {
        Group {
            if isLoading {
                ProgressView()
            } else if let profile {
                Text(profile.name)
            } else {
                Text("Nabigong mag-load")
            }
        }
        .task {
            defer { isLoading = false }
            do {
                profile = await APIClient().fetchProfile()
            } catch {
                // profile ay nananatiling nil
            }
        }
    }
}

Parallel na pag-load gamit ang TaskGroup

swift
struct DashboardView: View {
    @State var stats: DashboardStats?
    
    var body: some View {
        Text("Dashboard")
            .task {
                stats = await Task {
                    await withThrowingTaskGroup { group in
                        group.addTask { await API().fetchUsers() }
                        group.addTask { await API().fetchOrders() }
                        group.addTask { await API().fetchRevenue() }
                        
                        return DashboardStats(
                            users: try await group.next(),
                            orders: try await group.next(),
                            revenue: try await group.next()
                        )
                    }
                }.value
            }
    }
}

Mga karaniwang pagkakamali sa .task

Ang pinakakaraniwang pagkakamali — pag-mutate ng mga UI property sa loob ng .task nang hindi lumilipat sa pangunahing thread. Kahit na awtomatikong ibinabalik ng SwiftUI ang mga update sa pangunahing thread kapag nagbago ang @State sa async na konteksto, ang direktang pagmamanipula ng mga elemento ng UIKit sa loob ng .task ay maaaring magdulot ng crash.

  • Nakalimutan ang try/catch — hindi awtomatikong hinahawakan ng .task ang mga error. Ang lahat ng function na nagtatapon ng error sa loob ay dapat na balot sa do/catch, kung hindi, ang application ay babagsak.
  • Race condition — kung maraming .task(id:) ang inilunsad na may iba't ibang id at ina-update ang parehong state, posible ang mga race condition. Gumamit ng hiwalay na property para sa iba't ibang source ng data.
  • Mahahabang synchronous na operasyon — hindi ginagawang asynchronous ng .task ang synchronous code. Kung may mabigat na synchronous na gawain sa loob ng .task, balutin ito sa Task.detached o ilipat sa isang hiwalay na async method.
  • Pagbalewala sa CancellationError — sa pagsusuri ng Task.checkCancellation(), ang error na CancellationError ay dapat na i-propagate pataas, hindi pigilan. Ang pagsugpo sa pagkansela ay maaaring humantong sa mga leak ng memorya.
swift
// ❌ Error: walang paghawak ng error
.task {
    let data = await fetchData() // bumagsak sa error!
    items = data
}

// ✅ Tama: do/catch
.task {
    do {
        items = await fetchData()
    } catch {
        errorMessage = error.localizedDescription
    }
}

Mga Madalas Itanong

Ano ang pagkakaiba sa pagitan ng .task at paggamit ng Task { } sa .onAppear?

.task ay awtomatikong kinakansela ang gawain kapag nawala ang View at sinusuportahan ang Structured Concurrency. Ang Task { } sa .onAppear ay nangangailangan ng manu-manong pag-iimbak ng referensya sa gawain at pagtawag ng cancel() sa .onDisappear. Ang .task ay mas madali ring basahin — malinaw nitong ipinapahiwatig na ang pag-load ng data ay bahagi ng lifecycle ng View.

Maaari bang gamitin ang .task para mag-subscribe sa publisher?

Oo, para mag-subscribe sa AsyncSequence o AsyncStream, gamitin ang for await value in publisher.values sa loob ng .task. Gumagana ito pareho sa async/await at sa Combine sa pamamagitan ng extension na Publisher.values. Kapag nawala ang View, awtomatikong magtatapos ang iteration at makakansela ang subscription.

Paano gumagana ang .task sa TabView — nagre-restart ba ang gawain kapag lumipat ng tab?

Oo, kung ang View sa loob ng TabView ay muling nililikha sa bawat paglipat ng tab. Mula iOS 18, ang TabView ay maaaring mag-imbak ng View sa memorya — sa kasong ito, hindi nagre-restart ang .task. Gamitin ang .task(id:) na may identifier ng tab kung kailangan mong mag-reload ng data sa bawat paglipat ng tab.

Ano ang mangyayari kung ang View na may .task ay mawala bago matapos ang request?

Kinakansela ng SwiftUI ang gawain kapag nawala ang View. Kung ang URLSession request ay nasa loob ng gawain, ito ay makakansela rin. Kung ang gawain ay hindi sumusuporta sa kooperatibong pagkansela (halimbawa, hindi sinusuri ang isCancelled), magpapatuloy ito, ngunit ang resulta nito ay hindi ilalapat sa state dahil wala na ang View.

Maaari bang gumamit ng maramihang .task sa isang View?

Oo, maaari kang magdagdag ng maramihang .task modifier sa isang View. Bawat isa ay lumilikha ng independiyenteng gawain. Ito ay maginhawa para sa paghihiwalay ng iba't ibang source ng data: isang .task para sa pag-load ng profile, pangalawa para sa pag-subscribe sa WebSocket, pangatlo para sa pag-monitor ng geolocation.

Buod

  • .task { } — SwiftUI modifier para sa mga asynchronous na operasyon na may awtomatikong pagkansela kapag nawala ang View.
  • Suporta sa async/await — sa loob ng .task ay available ang buong async na konteksto nang hindi nangangailangan ng Task wrapper.
  • .task(id:) — nagre-restart ng gawain kapag nagbago ang identifier, pinapalitan ang manu-manong .onChange.
  • Kooperatibong pagkansela — gamitin ang Task.checkCancellation() para suriin ang pagkansela sa loob ng gawain.
  • Structured Concurrency — sinusuportahan ng .task ang TaskGroup at parallel na operasyon na may pagkansela ng child task.
  • Pagpapalit sa .onAppear — para sa asynchronous na pag-load ng data, gamitin ang .task sa halip na kombinasyon ng .onAppear + Task + .onDisappear.
  • Paghawak ng error — lahat ng async na tawag sa loob ng .task ay dapat balot sa do/catch upang maiwasan ang crash.

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din