等待一个块加载完成,再加载更多。

6
我正在开发一个应用程序,需要分批加载数据,即首先加载5个项目,然后再继续加载另外5个,但我无法弄清如何实现。目前,我将我的项目列表分成块,每个块包含5个项目。目前for循环只是发送请求,但我希望等待响应,然后在for循环中继续执行。
我使用Alamofire,我的代码如下。
private func requestItemsForField(items: [Item], completion: @escaping (_ measurements: Array<Measurement>?, _ success: Bool) -> ()) {
    let userPackageId = UserManager.instance.selectedUserPackage.id
    let params = ["userPackageId": userPackageId]

    for field in fields {
    let url = apiURL + "images/\(field.id)"

    let queue = DispatchQueue(label: "com.response-queue", qos: .utility, attributes: [.concurrent])
    Alamofire.request(url, method: .get, parameters: params, headers: headers()).responseArray(queue: queue, completionHandler: { (response: DataResponse<[Item]>) in

        if let items = response.result.value as [Item]? {
            NotificationCenter.default.post(name: NSNotification.Name(rawValue: "itemsLoadedNotification"), object: nil)
            completion(items, true)
        }
        else {
            print("Request failed with error: \(response.result.error)")
            completion(nil, false)
        }
    })
    }
}

这是我将列表分块并传递给上方的地方。
 private func fetchAllMeasurements(completion: @escaping (_ measurements: [Item]?, _ done: Bool) -> ()) {
    let fieldSet = FieldStore.instance.data.keys
    var fieldKeys = [Item]()

    for field in fieldSet {
        fieldKeys.append(field)
    }

    // Create chunks of fields to load
    let fieldChunks = fieldKeys.chunkify(by: 5)

    var measurementsAll = [Measurement]()
    for fields in fieldChunks {
        requestItemsForField(fields: fields, completion: { (measurements, success) in
            if let currentMeasurement = measurements {
                measurementsAll.append(contentsOf: currentMeasurement)
            }
                completion(measurementsAll, true)
            }
        })
    }
}

我认为你需要在前端实现类似分页的功能。我建议你更新后端以支持分页,但如果你需要在前端实现它,请查看此链接 -> PagedArray - ridvankucuk
你可以尝试使用dispatch_group或dispatch_barrier。 - Archerlly
3个回答

2

你需要在请求中获取你将拥有的测量数量(例如服务器有34个测量),然后编写类似以下代码的内容

var serverMeasurementsCount = 1 //should be for first request

func requestData() {
    if self.measurements.count < self.serverMeasurementsCount {
        ...requestdata { data in
            self.serverMeasurementsCount = data["serverMeasurementsCount"]
            self.measurements.append(..yourData)
            self.requestData()
        }
    }

或者在完成处理程序之外或其他地方调用requestData

编辑:稍微修复了一下代码(serverMeasurementsCount = 1


1
不要使用for循环,看起来你需要像这样做:var index = 0开始,并调用requestItemsForField()发送fieldChunks[index]作为第一个参数。然后在完成处理程序中,检查是否有另一个数组元素,如果有,则再次调用requestItemsForField(),这次将fieldChunks[index+1]作为第一个参数发送。

但是如果我再次调用requestItemsForField(),我们只会在其中有另一个完成处理程序? - Recusiwe
如果我的想法行得通,你就不需要编写另一个完成处理程序了,因为每个完成处理程序都会生成对 requestItemsForField() 的另一个调用,当它完成时,当然也会运行其自己的完成处理程序。等到你到达数组的末尾时,它就会停止生成调用,因为你的完成处理程序中的代码仅在还剩至少一个数组元素时才会生成新的调用。总体效果是,在上一个调用完成之前,不会发生任何对 requestItemsForField() 的新调用,这是您最初的目标。 - ClayJ
更明确地说,每次requestItemsForField()到达其完成处理程序时,将会进行另一个对requestItemsForField()的调用。这就是为什么只需要编写一个完成处理程序。它不会无限制地继续下去,因为完成处理程序中的代码只有在还有另一个数组元素需要处理时才会调用该函数。 - ClayJ

0
一个解决方案是创建一个新的递归函数来填充项目,在闭包中添加一个新的布尔参数作为isComplete。然后在isComplete布尔值完成时调用该函数。为了打破递归函数,添加一个全局静态变量itemsCountMax,如果itemCountMax == itemsCount,则中断递归函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接