асинхронные запросы на Swift с использованием обработчика завершения и диспетчерского семафора

#swift #signals #dispatchsemaphore

#swift #сигналы #диспетчерский семафор

Вопрос:

Я пытаюсь отправлять async запросы на Swift, используя Alamofire's completion handler и DispatchSemaphores . Мне нужно получить ответ, а затем вернуть его другому методу, так что в основном это мой код

 func customRequest(zipCode: String) -> Bool{
    var response = false

    let dispatchQueueProcess = DispatchQueue.global(qos: .userInitiated)
    let semaphore = DispatchSemaphore(value: 1)
    
    dispatchQueueProcess.async {
        
        semaphore.wait()

        apiRequest(zipCode: zipCode) { apiResponse in
        
            if apiResponse != nil {
            
response = apiResponse
               
             
            } else {
                print("Server did not Response")
            }
            
            semaphore.signal()
            
            
        }
    }
    
    return response
}
 

Проблема в том, что запрос всегда возвращается false , потому что не ожидает apiRequest ответа… Есть ли у вас какие-либо идеи, как это исправить? Большое вам спасибо!

Ps. apiRequest возвращает «true» /»false»

Ответ №1:

Вы намерены создать синхронную функцию, которая будет блокировать и ждать, пока асинхронный запрос не завершится?

Если это так, вы почти на месте, но вам нужно изменить семафор, чтобы он начинался с 0, а не с 1 (уменьшение с 1 до 0 не остановит выполнение, оно должно быть отрицательным), и вам нужно переместить вызов wait() за пределы закрытия, иначе вы не остановите функциюот возврата и вместо этого будет блокировать завершение вашего закрытия.

 func customRequest(zipCode: String) -> Bool {
    var response = false
    
    let dispatchQueueProcess = DispatchQueue.global(qos: .userInitiated)
    let semaphore = DispatchSemaphore(value: 0)
    
    // Start async work on background thread, current function's thread 
    // execution point will then immediately move to the line 
    // after the closing brace 
    dispatchQueueProcess.async {
        apiRequest(zipCode: zipCode) { apiResponse in
            if let apiResponse = apiResponse {
                response = apiResponse
            } else {
                print("Server did not Response")
            }

            // Tell the original function's thread that it is OK to continue
            semaphore.signal()

        }
    }

    // Immediately wait for the semaphore after starting the async work so that
    // the customRequest(zipCode:) function pauses here until the semaphore is
    // signalled in the closure.
    semaphore.wait()
    
    // Once the semaphore.signal() call happens in the async closure execution
    // resumes, and the response variable can now be returned with the updated
    // value.
    return response
}
 

Возможно, вы обнаружите, что на самом деле не хотите создавать подобную синхронную функцию, потому что, если вы вызовете это из основной очереди, вы заморозите пользовательский интерфейс до получения ответа.

Редактировать: этот пример должен выполняться для копирования и вставки на игровой площадке

 import Foundation

// template function to simulate the API call
func apiRequest(zipCode: String, response: (Bool?)->Void) {
    sleep(3)
    response(true)
}

func customRequest(zipCode: String) -> Bool{
    var response = false
    
    let dispatchQueueProcess = DispatchQueue.global(qos: .userInitiated)
    let semaphore = DispatchSemaphore(value: 0)
    
    dispatchQueueProcess.async {
        apiRequest(zipCode: zipCode) { apiResponse in
            if let apiResponse = apiResponse {
                response = apiResponse
            } else {
                print("Server did not Response")
            }
            semaphore.signal()

        }
    }
    
    semaphore.wait()
    
    return response
}

print("Result: (customRequest(zipCode: "90030"))")
 

Комментарии:

1. это именно то, что я ищу! Позвольте мне попробовать завтра на работе, и я дам вам знать, работает ли ваше решение, надеюсь, это произойдет. Большое вам спасибо!

2. Не сработало, чувак, единственное, что произошло, это стоп-экран, так что я попробую то, что сказал Фланкер.

3. Когда вы говорите «экран замораживания», что вы имеете в виду? Вы имеете в виду, что пользовательский интерфейс блокируется при вызове этой функции? Если это так, то этого следовало бы ожидать, если ваша цель действительно состоит в том, чтобы создать блокирующую оболочку вокруг вызова. Когда я запускаю это на игровой площадке, все работает так, как описано. Обновил приведенный выше пример тем, что вы можете скопировать и вставить непосредственно на игровую площадку.

4. Я имею в виду, что процесс API не отвечал только в том случае, если я использую обработчик завершения вместо возвращаемого примера

Ответ №2:

Ваш оператор return находится за пределами обработчика завершения, поэтому он будет выполняться немедленно, возвращая его начальное значение false, а не возвращая результат запроса, как вам требуется.

Вы должны удалить свойство response и обработать ответ внутри обработчика завершения.