অ্যাসিক্রোনাস নেটওয়ার্কের অনুরোধগুলির সাথে লুপের জন্য দ্রুত গতি পর্যন্ত অপেক্ষা করুন uting


159

আমি চাইব যে ইন লুপটি ফায়ারবেসে নেটওয়ার্কের অনুরোধগুলির একটি গুচ্ছটি প্রেরণ করবে, তারপরে পদ্ধতিটি কার্যকর হওয়ার পরে ডেটাটিকে একটি নতুন ভিউ নিয়ামকের কাছে প্রেরণ করুন। আমার কোডটি এখানে:

var datesArray = [String: AnyObject]()

for key in locationsArray {       
    let ref = Firebase(url: "http://myfirebase.com/" + "\(key.0)")
    ref.observeSingleEventOfType(.Value, withBlock: { snapshot in

        datesArray["\(key.0)"] = snapshot.value
    })
}
// Segue to new view controller here and pass datesArray once it is complete 

আমার দুজনের উদ্বেগ আছে। প্রথমত, লুপটির জন্য এবং নেটওয়ার্কের সমস্ত অনুরোধগুলি সম্পূর্ণ না হওয়া পর্যন্ত আমি কীভাবে অপেক্ষা করব? আমি অবজেক্টসিংএভেন্টঅফটাইপ ফাংশনটি সংশোধন করতে পারি না, এটি ফায়ারবেস এসডিকে অংশ। এছাড়াও, আমি লুপের বিভিন্ন পুনরাবৃত্তির থেকে তারিখের অ্যার্রে অ্যাক্সেস করার চেষ্টা করে এক ধরণের রেস শর্ত তৈরি করব (আশা করি তা বোঝায়?) আমি জিসিডি এবং এনএসওপ্রেশন সম্পর্কে পড়ছি তবে আমি তৈরি প্রথম অ্যাপটি হওয়ায় আমি কিছুটা হারিয়েছি।

দ্রষ্টব্য: ফায়ারবেসে অ্যাক্সেস করার জন্য কীগুলি থাকা আমার কাছে অবস্থানের অ্যারে একটি অ্যারে। এছাড়াও, এটি গুরুত্বপূর্ণ যে নেটওয়ার্ক অনুরোধগুলি অযৌক্তিকভাবে বন্ধ করা হয়। আমি পরবর্তী ভিউ কন্ট্রোলারে তারিখগুলি অ্যার্রে পাস করার আগে সমস্ত অ্যাসিনক্রোনাস অনুরোধগুলি শেষ হওয়া পর্যন্ত অপেক্ষা করতে চাই।

উত্তর:


338

আপনার সমস্ত অনুরোধগুলি শেষ হয়ে গেলে আপনি অ্যাসিঙ্ক্রোনাস কলব্যাক ফায়ার করতে প্রেরণ দলগুলি ব্যবহার করতে পারেন ।

একাধিক নেটওয়ার্কিংয়ের অনুরোধ সমস্ত শেষ হয়ে গেলে অবিচ্ছিন্নভাবে একটি কলব্যাক কার্যকর করতে প্রেরণ দলগুলি ব্যবহার করার একটি উদাহরণ এখানে Here

override func viewDidLoad() {
    super.viewDidLoad()

    let myGroup = DispatchGroup()

    for i in 0 ..< 5 {
        myGroup.enter()

        Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
            print("Finished request \(i)")
            myGroup.leave()
        }
    }

    myGroup.notify(queue: .main) {
        print("Finished all requests.")
    }
}

আউটপুট

Finished request 1
Finished request 0
Finished request 2
Finished request 3
Finished request 4
Finished all requests.

এটি দুর্দান্ত কাজ করেছে! ধন্যবাদ! আমি যখন তারিখআরাই আপডেট করার চেষ্টা করছি তখন আমি কোনও রেসের শর্তে চলে যাব কিনা আপনার কি ধারণা আছে?
জোশ

আমি মনে করি না যে এখানে একটি রেসের শর্ত রয়েছে কারণ সমস্ত অনুরোধগুলি datesArrayআলাদা কী ব্যবহার করে মান যুক্ত করে।
paulvs

1
@ জোসের রেসের শর্ত সম্পর্কে: একটি রেসের শর্তটি ঘটে, যদি একই মেমরির অবস্থানটি বিভিন্ন থ্রেড থেকে অ্যাক্সেস করা হবে, যেখানে কমপক্ষে সিঙ্ক্রোনাইজেশন ব্যবহার না করে কমপক্ষে একটি অ্যাক্সেস রাইটিং। যদিও একই সিরিয়াল প্রেরণের সারির মধ্যে সমস্ত অ্যাক্সেস সিঙ্ক্রোনাইজ করা হয়। সিঙ্ক্রোনাইজেশনটি প্রেরণকাতারি A তে ঘটে যাওয়া মেমরি অপারেশনগুলির সাথে ঘটে যা অন্য প্রেরণের জন্য সারি বিতে জমা হয় B ক সারিতে সমস্ত ক্রিয়াকলাপ ক পরে বি সমন্বয় করা হয়, সুতরাং আপনি যদি সমাধানটি দেখেন তবে এটি স্বয়ংক্রিয়ভাবে গ্যারান্টিযুক্ত নয় যে অ্যাকসেসগুলি সিঙ্ক্রোনাইজ হয়েছে। ;)
কাউচডেলিভার

@ জোশ, সচেতন থাকুন যে "রেসট্র্যাক প্রোগ্রামিং" এককথায় বুদ্ধিমানভাবে কঠিন। কেবল তাত্ক্ষণিকভাবে বলা সম্ভব হয় না "সেখানে আপনার কোনও সমস্যা / সমস্যা নেই"। শখের প্রোগ্রামারদের জন্য: "সরল" সর্বদা এমন উপায়ে কাজ করে যার অর্থ রেসট্র্যাক সমস্যাগুলি সহজ, অসম্ভব। (উদাহরণস্বরূপ, "একবারে একবারে একটি জিনিস করুন" ইত্যাদি things) এমনকি এটি করা একটি বিশাল প্রোগ্রামিং চ্যালেঞ্জ।
ফ্যাটি

দুর্দান্ত শীতল। তবে আমার একটা প্রশ্ন আছে। মনে করুন অনুরোধ 3 এবং অনুরোধ 4 ব্যর্থ হয়েছে (যেমন সার্ভার ত্রুটি, অনুমোদনের ত্রুটি, কিছু) তবে কেবলমাত্র অবশিষ্ট অনুরোধের জন্য পুনরায় লুপের জন্য কীভাবে অনুরোধ করবেন (অনুরোধ 3 এবং অনুরোধ 4)?
জেডি।

43

এক্সকোড 8.3.1 - সুইফট 3

এটি পলভগুলির স্বীকৃত উত্তর, সুইফ্ট 3 এ রূপান্তরিত:

let myGroup = DispatchGroup()

override func viewDidLoad() {
    super.viewDidLoad()

    for i in 0 ..< 5 {
        myGroup.enter()
        Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON { response in
            print("Finished request \(i)")
            myGroup.leave()
        }
    }

    myGroup.notify(queue: DispatchQueue.main, execute: {
        print("Finished all requests.")
    })
}

1
হাই, এটি 100 টি অনুরোধ বলার জন্য কাজ করে? বা 1000? কারণ আমি প্রায় 100 টি অনুরোধ নিয়ে এটি করার চেষ্টা করছি এবং অনুরোধটি সম্পূর্ণ হওয়ার পরে ক্রাশ হচ্ছে।
lopes710

আমি দ্বিতীয় @ lopes710-- এটি সমস্ত অনুরোধকে সমান্তরালভাবে কাজ করার অনুমতি দেয়, তাই না?
ক্রিস প্রিন্স

যদি আমার কাছে 2 টি নেটওয়ার্ক অনুরোধ থাকে, একজনের সাথে অন্যের সাথে নীস্ট থাকে, লুপের জন্য একটি এর অভ্যন্তরে, তবে কীভাবে নিশ্চিত করা যায় যে লুপের প্রতিটি পুনরাবৃত্তির জন্য, উভয় অনুরোধগুলি সম্পন্ন হয়েছে। ?
আওয়েস ফায়াজ

@ চ্যানেল, দয়া করে এই অর্ডারটি পাওয়ার কোনও উপায় আছে?
ইস্রায়েল মেশিলিয়া

41

সুইফট 3 বা 4

আপনি যদি অর্ডারগুলি সম্পর্কে চিন্তা না করেন তবে @ পলভসের উত্তরটি ব্যবহার করুন , এটি পুরোপুরি কার্যকর।

অন্যথায় যদি কেউ একযোগে আগুন দেওয়ার পরিবর্তে ফলাফল পেতে চায় তবে কোডটি এখানে

let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "any-label-name")
let dispatchSemaphore = DispatchSemaphore(value: 0)

dispatchQueue.async {

    // use array categories as an example.
    for c in self.categories {

        if let id = c.categoryId {

            dispatchGroup.enter()

            self.downloadProductsByCategory(categoryId: id) { success, data in

                if success, let products = data {

                    self.products.append(products)
                }

                dispatchSemaphore.signal()
                dispatchGroup.leave()
            }

            dispatchSemaphore.wait()
        }
    }
}

dispatchGroup.notify(queue: dispatchQueue) {

    DispatchQueue.main.async {

        self.refreshOrderTable { _ in

            self.productCollectionView.reloadData()
        }
    }
}

আমার অ্যাপ্লিকেশনটিকে একটি এফটিপি সার্ভারে একাধিক ফাইল প্রেরণ করতে হবে, এতে প্রথমে লগ ইন করাও অন্তর্ভুক্ত। এই পদ্ধতির গ্যারান্টি দেয় যে অ্যাপ্লিকেশন কেবল একবারে (প্রথম ফাইলটি আপলোড করার আগে) একবারে লগইন করে, মূলত একই সময়ে (একইসাথে "অর্ডারযুক্ত" পদ্ধতির সাথে) যা ত্রুটিগুলি ট্রিগার করে। ধন্যবাদ!
নেফ

যদিও আমি একটি প্রশ্ন পেয়েছি: আপনি dispatchSemaphore.signal()ছেড়ে যাওয়ার আগে বা পরে কি করেন তা কি ব্যাপার dispatchGroup? আপনি ভাবেন যে যত তাড়াতাড়ি দেরি-তীরটি অবরুদ্ধ করা ভাল তবে আমি নিশ্চিত নই যে গ্রুপটি ছেড়ে কীভাবে তাতে হস্তক্ষেপ করে। আমি উভয় আদেশ পরীক্ষা করেছি এবং এটি কোনও পার্থক্য বলে মনে হচ্ছে না।
নেফ

16

বিস্তারিত

  • এক্সকোড 10.2.1 (10E1001), সুইফট 5

সমাধান

import Foundation

class SimultaneousOperationsQueue {
    typealias CompleteClosure = ()->()

    private let dispatchQueue: DispatchQueue
    private lazy var tasksCompletionQueue = DispatchQueue.main
    private let semaphore: DispatchSemaphore
    var whenCompleteAll: (()->())?
    private lazy var numberOfPendingActionsSemaphore = DispatchSemaphore(value: 1)
    private lazy var _numberOfPendingActions = 0

    var numberOfPendingTasks: Int {
        get {
            numberOfPendingActionsSemaphore.wait()
            defer { numberOfPendingActionsSemaphore.signal() }
            return _numberOfPendingActions
        }
        set(value) {
            numberOfPendingActionsSemaphore.wait()
            defer { numberOfPendingActionsSemaphore.signal() }
            _numberOfPendingActions = value
        }
    }

    init(numberOfSimultaneousActions: Int, dispatchQueueLabel: String) {
        dispatchQueue = DispatchQueue(label: dispatchQueueLabel)
        semaphore = DispatchSemaphore(value: numberOfSimultaneousActions)
    }

    func run(closure: ((@escaping CompleteClosure) -> Void)?) {
        numberOfPendingTasks += 1
        dispatchQueue.async { [weak self] in
            guard   let self = self,
                    let closure = closure else { return }
            self.semaphore.wait()
            closure {
                defer { self.semaphore.signal() }
                self.numberOfPendingTasks -= 1
                if self.numberOfPendingTasks == 0, let closure = self.whenCompleteAll {
                    self.tasksCompletionQueue.async { closure() }
                }
            }
        }
    }

    func run(closure: (() -> Void)?) {
        numberOfPendingTasks += 1
        dispatchQueue.async { [weak self] in
            guard   let self = self,
                    let closure = closure else { return }
            self.semaphore.wait(); defer { self.semaphore.signal() }
            closure()
            self.numberOfPendingTasks -= 1
            if self.numberOfPendingTasks == 0, let closure = self.whenCompleteAll {
                self.tasksCompletionQueue.async { closure() }
            }
        }
    }
}

ব্যবহার

let queue = SimultaneousOperationsQueue(numberOfSimultaneousActions: 1, dispatchQueueLabel: "AnyString")
queue.whenCompleteAll = { print("All Done") }

 // add task with sync/async code
queue.run { completeClosure in
    // your code here...

    // Make signal that this closure finished
    completeClosure()
}

 // add task only with sync code
queue.run {
    // your code here...
}

সম্পূর্ণ নমুনা

import UIKit

class ViewController: UIViewController {

    private lazy var queue = { SimultaneousOperationsQueue(numberOfSimultaneousActions: 1,
                                                           dispatchQueueLabel: "AnyString") }()
    private weak var button: UIButton!
    private weak var label: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(frame: CGRect(x: 50, y: 80, width: 100, height: 100))
        button.setTitleColor(.blue, for: .normal)
        button.titleLabel?.numberOfLines = 0
        view.addSubview(button)
        self.button = button

        let label = UILabel(frame: CGRect(x: 180, y: 50, width: 100, height: 100))
        label.text = ""
        label.numberOfLines = 0
        label.textAlignment = .natural
        view.addSubview(label)
        self.label = label

        queue.whenCompleteAll = { [weak self] in self?.label.text = "All tasks completed" }

        //sample1()
        sample2()
    }

    func sample1() {
        button.setTitle("Run 2 task", for: .normal)
        button.addTarget(self, action: #selector(sample1Action), for: .touchUpInside)
    }

    func sample2() {
        button.setTitle("Run 10 tasks", for: .normal)
        button.addTarget(self, action: #selector(sample2Action), for: .touchUpInside)
    }

    private func add2Tasks() {
        queue.run { completeTask in
            DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + .seconds(1)) {
                DispatchQueue.main.async { [weak self] in
                    guard let self = self else { return }
                    self.label.text = "pending tasks \(self.queue.numberOfPendingTasks)"
                }
                completeTask()
            }
        }
        queue.run {
            sleep(1)
            DispatchQueue.main.async { [weak self] in
                guard let self = self else { return }
                self.label.text = "pending tasks \(self.queue.numberOfPendingTasks)"
            }
        }
    }

    @objc func sample1Action() {
        label.text = "pending tasks \(queue.numberOfPendingTasks)"
        add2Tasks()
    }

    @objc func sample2Action() {
        label.text = "pending tasks \(queue.numberOfPendingTasks)"
        for _ in 0..<5 { add2Tasks() }
    }
}

5

এই উদ্দেশ্যে আপনাকে semaphores ব্যবহার করতে হবে।

 //Create the semaphore with count equal to the number of requests that will be made.
let semaphore = dispatch_semaphore_create(locationsArray.count)

        for key in locationsArray {       
            let ref = Firebase(url: "http://myfirebase.com/" + "\(key.0)")
            ref.observeSingleEventOfType(.Value, withBlock: { snapshot in

                datesArray["\(key.0)"] = snapshot.value

               //For each request completed, signal the semaphore
               dispatch_semaphore_signal(semaphore)


            })
        }

       //Wait on the semaphore until all requests are completed
      let timeoutLengthInNanoSeconds: Int64 = 10000000000  //Adjust the timeout to suit your case
      let timeout = dispatch_time(DISPATCH_TIME_NOW, timeoutLengthInNanoSeconds)

      dispatch_semaphore_wait(semaphore, timeout)

     //When you reach here all request would have been completed or timeout would have occurred.

3

সুইফট 3: আপনি এই পথে semaphores ব্যবহার করতে পারে। এটি কখনই এবং কী কী প্রক্রিয়াগুলি সম্পন্ন হয় তার সঠিক ট্র্যাক রাখতে পারে তা ছাড়াও এটি খুব সহায়ক ফলাফল। এটি আমার কোড থেকে বের করা হয়েছে:

    //You have to create your own queue or if you need the Default queue
    let persons = persistentContainer.viewContext.persons
    print("How many persons on database: \(persons.count())")
    let numberOfPersons = persons.count()

    for eachPerson in persons{
        queuePersonDetail.async {
            self.getPersonDetailAndSave(personId: eachPerson.personId){person2, error in
                print("Person detail: \(person2?.fullName)")
                //When we get the completionHandler we send the signal
                semaphorePersonDetailAndSave.signal()
            }
        }
    }

    //Here we will wait
    for i in 0..<numberOfPersons{
        semaphorePersonDetailAndSave.wait()
        NSLog("\(i + 1)/\(persons.count()) completed")
    }
    //And here the flow continues...

1

আমরা এটি পুনরাবৃত্তি সহ করতে পারি। নীচের কোড থেকে ধারণা পান:

var count = 0

func uploadImages(){

    if count < viewModel.uploadImageModelArray.count {
        let item = viewModel.uploadImageModelArray[count]
        self.viewModel.uploadImageExpense(filePath: item.imagePath, docType: "image/png", fileName: item.fileName ?? "", title: item.imageName ?? "", notes: item.notes ?? "", location: item.location ?? "") { (status) in

            if status ?? false {
                // successfully uploaded
            }else{
                // failed
            }
            self.count += 1
            self.uploadImages()
        }
    }
}

-1

প্রেরণ গোষ্ঠীটি ভাল তবে প্রেরিত অনুরোধের ক্রম এলোমেলো।

Finished request 1
Finished request 0
Finished request 2

আমার প্রকল্পের ক্ষেত্রে, প্রতিটি অনুরোধ প্রবর্তন করা প্রয়োজন সঠিক ক্রম। এটি যদি কাউকে সহায়তা করতে পারে:

public class RequestItem: NSObject {
    public var urlToCall: String = ""
    public var method: HTTPMethod = .get
    public var params: [String: String] = [:]
    public var headers: [String: String] = [:]
}


public func trySendRequestsNotSent (trySendRequestsNotSentCompletionHandler: @escaping ([Error]) -> () = { _ in }) {

    // If there is requests
    if !requestItemsToSend.isEmpty {
        let requestItemsToSendCopy = requestItemsToSend

        NSLog("Send list started")
        launchRequestsInOrder(requestItemsToSendCopy, 0, [], launchRequestsInOrderCompletionBlock: { index, errors in
            trySendRequestsNotSentCompletionHandler(errors)
        })
    }
    else {
        trySendRequestsNotSentCompletionHandler([])
    }
}

private func launchRequestsInOrder (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], launchRequestsInOrderCompletionBlock: @escaping (_ index: Int, _ errors: [Error] ) -> Void) {

    executeRequest(requestItemsToSend, index, errors, executeRequestCompletionBlock: { currentIndex, errors in
        if currentIndex < requestItemsToSend.count {
            // We didn't reach last request, launch next request
            self.launchRequestsInOrder(requestItemsToSend, currentIndex, errors, launchRequestsInOrderCompletionBlock: { index, errors in

                launchRequestsInOrderCompletionBlock(currentIndex, errors)
            })
        }
        else {
            // We parse and send all requests
            NSLog("Send list finished")
            launchRequestsInOrderCompletionBlock(currentIndex, errors)
        }
    })
}

private func executeRequest (_ requestItemsToSend: [RequestItem], _ index: Int, _ errors: [Error], executeRequestCompletionBlock: @escaping (_ index: Int, _ errors: [Error]) -> Void) {
    NSLog("Send request %d", index)
    Alamofire.request(requestItemsToSend[index].urlToCall, method: requestItemsToSend[index].method, parameters: requestItemsToSend[index].params, headers: requestItemsToSend[index].headers).responseJSON { response in

        var errors: [Error] = errors
        switch response.result {
        case .success:
            // Request sended successfully, we can remove it from not sended request array
            self.requestItemsToSend.remove(at: index)
            break
        case .failure:
            // Still not send we append arror
            errors.append(response.result.error!)
            break
        }
        NSLog("Receive request %d", index)
        executeRequestCompletionBlock(index+1, errors)
    }
}

কল করুন:

trySendRequestsNotSent()

ফলাফল :

Send list started
Send request 0
Receive request 0
Send request 1
Receive request 1
Send request 2
Receive request 2
...
Send list finished

আরও ইনফোগুলি দেখুন: সংক্ষেপে

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.