Синтаксис для переноса обработчика завершения в асинхронную отправку

У меня есть обработчик завершения, который мне нужно присвоить свойству, но я хочу, чтобы он выполнялся асинхронно.

Если бы у меня не было этого требования, я бы написал:

request.completionBlock = completionBlock

Но так как у меня есть это требование, я должен написать это

request.completionBlock = { response, error in
  DispatchQueue.main.async {
    completionBlock(response, error)
  }
}

что кажется избыточным и небыстрым.

Нет ли более простого синтаксиса? Я хотел бы написать что-то вроде

request.completionBlock = completionBlock.map(DispatchQueue.main.async)

Могу ли я выразить свою потребность таким простым способом?


person KPM    schedule 08.11.2016    source источник
comment
Является ли request экземпляром типа, который вы можете изменить, или он должен быть его расширением/подклассом?   -  person DavidA    schedule 16.11.2016


Ответы (3)


Для этого нет встроенного синтаксиса, но вы всегда можете определить общую функцию или оператор, чтобы включить что-то в этом роде.

Например:

infix operator >

func ><T>(left:@escaping (T)->(), right:DispatchQueue) -> (T)->() {
  return { input in
    right.async { left(input) }
  }
}

С указанным выше пользовательским оператором ваш код может быть:

request.completionBlock = completionBlock > DispatchQueue.main

что, я думаю, является общим чувством, которое вы ищете.

person Daniel Hall    schedule 16.11.2016
comment
Я как раз собирался предложить сформулировать это наоборот :) Спасибо! - person KPM; 24.11.2016
comment
С -> у меня была синтаксическая ошибка. Только с > (или §, или как-то так, все выглядит нормально). Поэтому я обновил ответ. - person KPM; 24.11.2016
comment
И на самом деле нам не нужен MultiplicationPrecedence. Это даже нежелательно по двум причинам: 1/ мы не хотим иметь ассоциативность, потому что не имеет смысла писать блок ‹ очередь ‹ очередь. И 2/ мы фактически перегружаем существующий оператор, поэтому мы не хотим давать ему другое правило приоритета. - person KPM; 25.11.2016
comment
@KPM Хороший вопрос! Исходный пример кода был пользовательским оператором, а не перегрузкой существующего, но приоритет по умолчанию был бы более подходящим. Или, во всяком случае, более низкий приоритет, который поддерживал бы функциональное связывание нескольких замыканий вместе в цепочке перед переносом выполнения в очередь отправки в качестве последнего шага. - person Daniel Hall; 25.11.2016

Вот расширение

extension DispatchQueue {
    func asyncClosure<T>(_ closure:@escaping (T) -> Void) -> (T) -> Void {
        return {i in self.async {closure(i)}}
    }
}

который позволяет вам сделать это:

request.completionBlock = DispatchQueue.main.asyncClosure(completionBlock)
person bzz    schedule 20.11.2016
comment
Этот ответ тоже был очень полезен. Я долго колебался между вашим и Дэниелом Холлом. В конце концов я решил присудить награду Даниэлю. Большое спасибо за альтернативу! - person KPM; 24.11.2016

У вас есть контроль над классом request? Если нет, то я думаю, что вам нужно стиснуть зубы и явно асинхронно отправить себя (и явное — это хорошо, или, по крайней мере, оно есть в python :-)) или определите свое собственное сокращение, как предлагает Дэниел Холл.

Если у вас есть контроль, то я думаю, что стоит предложить просто изменить API вашего класса request, чтобы он гарантировал, что обработчик завершения вызывается в основном потоке. В конце концов, обработчики завершения должны быть быстрыми, и в любом случае это часто то, что вам нужно.

person daphtdazz    schedule 17.11.2016