簡體   English   中英

"Swift async\/await 在 for 循環或映射中"

[英]Swift async/await in a for loop or map

我有這個模型:

struct ExactLocation {
    var coordinates: CLLocationCoordinate2D? {
        get async throws {
            let geocoder = CLGeocoder()
            let placemark = try await geocoder.geocodeAddressString(address)
            let mark = MKPlacemark(placemark: placemark.first!)
            return mark.coordinate
        }
    }
}
struct Property {
    let exactLocation: ExactLocation?
}

您可以在此處使用TaskGroup<\/code> ,它是一種AsyncSequence<\/code> 。 每個屬性的“獲取坐標”是您應該添加到任務組的任務。 然后,您可以以任何您喜歡的方式轉換任務(例如compactMap<\/code> )。 最后,您可以使用reduce<\/code>將任務組轉換為數組,然后await<\/code> :

private func addAnnotations(for properties: [Property]) async {
    let coordinates = await withTaskGroup(
        of: CLLocationCoordinate2D?.self) { group -> [CLLocationCoordinate2D] in
        for property in properties {
            group.addTask { try? await property.exactLocation?.coordinates }
        }
        return await group.compactMap { $0 }
        .reduce(into: []) { $0.append($1) }
    }
}

首先,請注意您執行的請求數量。 文檔說

  • 最多為任何一個用戶操作發送一個地理編碼請求。

  • 如果用戶執行涉及對同一位置進行地理編碼的多個操作,請重用初始地理編碼請求的結果,而不是為每個操作啟動單獨的請求。

  • 當您想要自動更新用戶的當前位置時(例如當用戶移動時),僅當用戶移動了相當長的距離並且經過了合理的時間后才發出新的地理編碼請求。 例如,在典型情況下,您每分鍾不應發送超過一個地理編碼請求。

舊的位置和地圖編程指南說:

相同的CLGeocoder對象可用於啟動任意數量的地理編碼請求,但對於給定的地理編碼器,一次只能激活一個請求。

所以,快速發出一系列地理定位請求的整個想法可能是不謹慎的,即使你只做幾個,我也傾向於避免同時執行它們。 所以,我會考慮一個簡單for循環,例如:

func addAnnotations(for addresses: [String]) async throws {
    let geocoder = CLGeocoder()
    
    for address in addresses {
        if 
            let placemark = try await geocoder.geocodeAddressString(address).first,
            let coordinate = placemark.location?.coordinate
        {
            let annotation = MKPointAnnotation()
            annotation.title = placemark.name
            annotation.coordinate = coordinate
            
            // ...
            
            // you might even want to throttle your requests, e.g.
            //
            // try await Task.sleep(nanoseconds: nanoseconds) 
        }
    }
}

從技術上講,您可以使用計算屬性方法。 現在,我在任何地方都沒有在您的模型中看到address ,但讓我們想象一下:

struct Property {
    let address: String
    
    var coordinate: CLLocationCoordinate2D? {
        get async throws {
            let geocoder = CLGeocoder()
            return try await geocoder
                .geocodeAddressString(address)
                .first?.location?.coordinate
        }
    }
}

(注意強制展開運算符的消除和另一個地標的不必要實例化。)

然后你可以這樣做:

func addAnnotations(for properties: [Property]) async throws {
    for property in properties {
        if let coordinate = try await property.coordinate {
            let annotation = MKPointAnnotation()
            annotation.coordinate = coordinate
            ...
        }
    }
}

我對這種方法並不瘋狂(因為我們在計算屬性中隱藏了具有各種約束的限速CLGeocoder請求;如果您重復訪問相同的屬性,將發出重復的地理編碼器請求,Apple 明確建議我們避免這種情況) . 但是async屬性在技術上也有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM