2016-05-03 7 views
2

Я много искал, чтобы узнать, как другие решили эту проблему, но, к сожалению, я не нашел ответа на этот конкретный вопрос. Я бы очень признателен вам за вашу помощь.Как заставить один метод ждать другого (асинхронного) метода до завершения выполнения?

Реферат: У меня есть два метода в моем классе, method1 и method2. Мне нужно вызвать функцию async внутри метода1. Затем код продолжает выполнение и достигает метода2. Но в методе 2 есть случаи, когда мне нужно использовать результат этого асинхронного вызова в методе 1, поэтому мне нужно убедиться, что асинхронный вызов в методе 1 завершил работу перед остальной частью метода2.

Я знаю, что одним из способов является использование семафоров, а другим способом является использование блоков завершения. Но я хочу сделать это наиболее общим образом, потому что будут другие методы, похожие на method2, которые снова должны будут дождаться завершения асинхронного вызова метода method1 до продолжения выполнения. По той же причине я не могу просто вызвать функцию async внутри самого метода2 и поместить оставшуюся часть метода в блок завершения.

Вот приблизительное представление о том, что я пытаюсь сделать. Я был бы признателен, если кто-то добавит завершение блоков в этот псевдокод, чтобы я мог видеть четкую картину того, как все будет работать. BTW, method1 и method2 (и все другие методы в этом классе) находятся в одном потоке (но не в основном потоке).

@implementation someClass 

-(void)method1 { 
    for (each item in the ivar array) { 
     if (condition C is met on that item) { 
      some_independent_async_call(item, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(int result, int *someArray) { 
       if (result > 0) { 
        // The async call worked correctly, we will need someArray 
       } 
       else { 
        // We didn't get someArray that we wanted. 
       } 
      }); 
     } 
    } 
} 

-(void)method2 { 
    // Select one item in the ivar array based on some criteria. 
    if (condition C is met on that item) { 
     // Wait for some_independent_async_call() in method1 to complete. 
     // Then use someArray in the rest of the code. 
    } 
    else { 
     // Simply continue the rest of the code. 
    } 
} 

@end 

Обновление: Я знаю, что может сигнализировать семафор после того, как вызов асинхронной завершается, и я могу ждать того же семафора в method2, но я хочу использовать блок завершения вместо потому что я думаю, что было бы более общий характер, особенно если в этом классе есть другие методы, подобные методу2. Может ли кто-нибудь добавить к этому блоку блокировку, поскольку у меня возникают проблемы с этой работой?

+2

Вашей 'логика for' петля заставляет меня думать, что это может быть то, что вы ищете: «* Использование групп отправки - группировка блоков позволяет выполнять агрегатную синхронизацию. Ваше приложение может отправлять несколько блоков и отслеживать, когда они все завершатся, даже если они могут выполняться в разных очередях. Такое поведение может быть полезно, когда прогресс не может быть сделано до тех пор, пока все указанные задачи не будут завершены. * « –

+0

Спасибо, но у меня есть только один блок, который нужно заполнить, и несколько мест, где его результат должен использоваться. Можете ли вы добавить к указанному выше псевдокоду, как группы отправки будут работать в этом случае? – makingCodeWork

+0

Ваш асинхронный вызов находится внутри цикла. Вы имеете в виду, что «условие С» верно только для одного элемента? –

ответ

1

Исходя из вашего кода, похоже, что у вас есть контроль над асинхронной отправкой.

Вместо some_independent_async_call использование dispatch_sync, который будет блокировать выполнение текущего потока до тех пор, данный блок не завершит

using dispatch_sync in Grand Central Dispatch

Однако, если вы не имеете контроля над асинхронным вызовом, и вы на самом деле захода в метод на объекте, который затем идет, и вызывает dispatch_async; у вас нет выбора использовать блок завершения, шаблон обратного вызова или семафор, как вы заявили

+0

Спасибо. Я не контролирую асинхронный вызов, это просто метод async из другого места, который мне нужно использовать. Не могли бы вы показать мне, как использовать блок завершения здесь, поскольку я, похоже, не прав, когда пытаюсь? – makingCodeWork

+0

Вы должны просто вызвать 'method2' из отправленного блока' method1', в конце блока –

1

Итак, если я правильно понял вашу проблему, у вас есть список «Элементы» и один asynchronous. Задача принимает параметр item и вычисляет некоторый «результат» (массив Ints).

Теперь для каждого «элемента» будет оценено логическое значение, которое определяет, следует ли запускать задачу с этим элементом в качестве аргумента.

По завершении каждой задачи (ваш some_independent_async_call) вы хотите вызвать продолжение (возможно, используя результат соответствующей завершенной задачи).

Ну, конечно, это можно реализовать с помощью групп отправки, обработчиков завершения, NSOperations и т. Д. Но это быстро станет довольно сложным и подверженным ошибкам, особенно если вы хотите обрабатывать ошибки и, возможно, реализовать средства для отмены задачи, если это необходимо. Но так как это становится настолько невероятно простым с «Futures», я предложу это решение.

Обратите внимание, что «фьючерсы» не включены в стандартную библиотеку Swift. Тем не менее, существует несколько сторонних библиотек. Здесь я буду использовать "Scala-like" futures. Обратите внимание, что это также можно реализовать с помощью «Обещаний» аналогичным образом. См. Также wiki Futures and Promises.

Используя фьючерсы, задача имеет следующую подпись:

typealias TaskType = (Item) -> Future<SomeResult> 

Будущее является «заполнитель» для значения, которое будет вычислено позже основной асинхронной задачи. Задача также может выйти из строя и вернуть значение вместо значения. То есть, будущее будет в конечном итоге завершено либо с результатом, либо с ошибкой.

Пусть задача:

func task(item: Item) -> Future<SomeResult> { 
    let promise = Promise<SomeResult>() 
    fetchSomeResult(item.url) { (result, error) in 
     if let result = result { 
      promise.fulfill(result) 
     } else { 
      promise.reject(error!) 
     } 
    } 
    return promise.future! 
} 

кажется проще, фильтровать массив, чтобы получить фактические «Items», которые начинают задачу:

let activeItems = items.filter { $0.foo == something } 

Получить массив фьючерсов:

let futures = activeItems.map { task($0) } 

Вышеупомянутый оператор запускает асинхронные задачи, каждый из которых соответствует соответствующему элементу и возвращает массив o f, чей тип [Future<SomeResult>]. В настоящее время фьючерсы еще не завершены завершено. Это произойдет в конечном итоге для каждого будущего, когда основное задание закончится.

Теперь добавьте продолжение к каждому будущему, которое вызывается, когда задача успешно, а также добавление обработчика ошибок, который вызывается, когда один происходит:

futures.forEach { future in 
    future.map { result in 
     // This will be entered for each task, once it 
     // finished successfully. 
     // Handle result (should be your array of ints) 
     // This is where you implement the "method2" part 
     // but for each item separately. 
     // Note: if you need the "item" as well, use a 
     // task which returns a tuple: 
     // task(item: Item) -> Future<(Item, SomeResult)> 
    }.onFailure { error in 
     // handle error. This is an error returned from the task. 
    } 
} 
+0

Благодарим вас за подробное описание того, как использовать фьючерсы в этом коде. Боюсь, я не могу использовать Swift для этой конкретной задачи, но я буду иметь это в виду на будущее! – makingCodeWork