在 Swift 中,如何在点击时发送网络请求,使得每个请求之间有几秒钟的延迟?

问题描述 投票:0回答:1

我创建了这个简单的示例来展示该问题。

我有 4 个按钮,用于点赞、收藏、举报和隐藏。用户可以随时点击任意按钮,点击任意次数。但是,在点击之后发送的网络请求之间必须始终有 2 秒的最小延迟。此外,所有请求都必须按顺序发送。

以下内容似乎可行,但有时,它不遵守 2 秒延迟并同时发送多个请求,中间没有任何延迟。我很难弄清楚是什么原因造成的。有一个更好的方法吗?或者说这有什么问题?

import UIKit
import SnapKit

extension UIControl {
    func addAction(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping()->()) {
        addAction(UIAction { (action: UIAction) in closure() }, for: controlEvents)
    }
}

let networkDelay = TimeInterval(2)
var requestWorkItems = [DispatchWorkItem]()
var lastRequestCompletionTime: Date?

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let stack = UIStackView()
        stack.axis = .vertical
        view.addSubview(stack)
        stack.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
        
        ["UPVOTE","FAVORITE","FLAG","HIDE"].forEach { title in
            let button = UIButton()
            button.setTitle(title, for: .normal)
            button.addAction { [weak self] in
                
                let urlString = "https://example.com/api?action=\(title)"
                
                self?.scheduleNetworkRequest(urlString: urlString)
            }
            button.titleLabel?.font = UIFont.systemFont(ofSize: 30, weight: .bold)
            button.snp.makeConstraints { make in
                make.height.equalTo(100)
            }
            stack.addArrangedSubview(button)
        }
    
    }
    
    func scheduleNetworkRequest(urlString : String) {
        let workItem = DispatchWorkItem { [weak self] in
            self?.sendNetworkRequest(urlString: urlString)
        }

        requestWorkItems.append(workItem)
            
        if Date().timeIntervalSince(lastRequestCompletionTime ?? .distantPast) > networkDelay {
            scheduleNextWork(delay: 0)
        }
    }
    
    func scheduleNextWork(delay : TimeInterval) {
        DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
            if let workItem = requestWorkItems.first {
                requestWorkItems.removeFirst()
                print("Tasks remaining: \(requestWorkItems.count)")
                workItem.perform()
            }
        }
    }

    func sendNetworkRequest(urlString : String) {
        guard let url = URL(string: urlString) else { return }
        
        let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
            if let error = error {
                print("Error: \(error)")
            } else if let _ = data {
                print("Completed: \(urlString) at: \(Date())")
            }
            lastRequestCompletionTime = Date()
            self?.scheduleNextWork(delay: networkDelay)
        }
        task.resume()
    }
    
}
ios swift grand-central-dispatch debouncing dispatch-queue
1个回答
0
投票

我会使用

AsyncStream
来记录 URL,并使用
Task
来消费流,中间等待 2 秒。

// add these properties to the view controller
var task: Task<Void, Never>?
let (stream, continuation) = AsyncStream.makeStream(of: URL.self)

在按钮的操作中,将 URL 添加到

continuation
:

button.addAction { [weak self] in
    self?.continuation.yield(URL(string: "https://example.com/api?action=\(title)")!)
}

并在

task
中初始化
viewDidLoad
,如下所示:

task = Task {
    for await url in stream {
        do {
            print("Sending request to \(url)")
            let (data, response) = try await URLSession.shared.data(from: url)
            // do something with data & response

            // wait 2 seconds before getting the next URL
            try await Task.sleep(for: .seconds(2))
        } catch is CancellationError {
            break
        } catch {
            // handle network errors...
        }
        if Task.isCancelled {
            break
        }
    }
}

最后,在

task?.cancel()
中拨打
deinit

© www.soinside.com 2019 - 2024. All rights reserved.