В Java есть будущее или будущее, которое может запускать задачу в новом потоке. Затем верните результат выполнения в исходный поток. Есть ли какие-либо возможности в Swift для этого?Есть ли что-то похожее на Java «Будущее» в Swift?
ответ
Вы смотрите в какой-то языковой конструкции называется Futures and promises. Вы можете найти некоторые примеры, как:
- https://bitbucket.org/al45tair/async (C# -like асинхронного/ОЖИДАНИЕ примитивов в Swift)
- https://github.com/mxcl/PromiseKit (Promise комплект http://promisekit.org/)
- упомянутые ранее https://github.com/Thomvis/BrightFutures
Однако язык сама пропускает такую функцию.
Не предусмотрено языком (имея в виду стандартную библиотеку), но вы можете уверенно катиться самостоятельно или просто использовать библиотеку, такие как https://github.com/Thomvis/BrightFutures
Существует также теперь FutureKit Подобно BrightFuture, но делает композицию более как BFTask
И я должен упомянуть Bolts BFTask, что в то время как написано в Objective-C также является хорошим кандидатом. (И теперь используется внутри Facebook iOS SDK)
Если бы Apple реализовала фьючерсы или обещания в Swift, они бы так сказали? В конце концов, они всегда избегают говорить о продуктах Future. ;)
В любом случае, исходный вопрос, как правило, касается способов выполнения асинхронной работы, а не обязательно о том, как это сделать с помощью модели стиля Futures/Promises. Итак, в то время как сторонние библиотеки, упомянутые в других ответах, отлично подходят для этой модели, вы также можете выполнять асинхронную работу без этой модели, используя те же самые встроенные API OS X iOS &, которые вы можете использовать в ObjC: dispatch
или NSOperation
. Например:
NSOperationQueue().addOperationWithBlock {
// do background work
NSOperationQueue.mainQueue().addOperationWithBlock {
// back to main thread for follow up work
}
}
я в конечном итоге следующим раствором (только IOS, SDK, Swift 3) на основе Operation
и OperationQueue
классов:
Вкратце: Обертывание кода в синхронном или асинхронном режиме. Цепочные операции с использованием служебного класса. Добавление операций в последовательную очередь.
В случае ошибки нет необходимости отменять текущую операцию, просто пропустите действительный код. Кроме того, асинхронные блоки выполнения должны вызывать обратный вызов finalize
, чтобы сообщить очередь операций о завершении. Необязательно DispatchQueue
может быть предоставлен в качестве параметра. Блок кода будет асинхронно выполняться в этой очереди.
fileprivate func publishProductOnWebsite(listing: Listing) {
var resultSKU: String?
var resultError: Swift.Error?
let chain = OperationsChain{ isExecuting, finalize in
let task = ServerAPI.create(publishInfo: listing.publishInfo) { sku, error in
guard isExecuting() else {
return // We are canceled. Nothing to do.
}
if let error = error {
resultError = error
} else if let sku = sku {
resultSKU = sku // Arbitrary thread. But OK as this example for serial operation queue.
}
finalize() // This will finish asynchronous operation
}
task.resume()
}
chain.thenAsync(blockExecutionQueue: DispatchQueue.main) { _, finalize in
if let sku = resultSKU {
listing.sku = sku
DBStack.mainContext.saveIfHasChanges(savingParent: true) { error in
resultError = error
finalize()
}
} else {
finalize()
}
}
chain.thenSync(blockExecutionQueue: DispatchQueue.main) { [weak self] in
if let error = resultError {
self?.handleError(error) // Executed on Main thread.
} else {
self?.trackPublish()
self?.eventHandler?(.publishCompleted)
}
}
operationQueue.cancelAllOperations()
operationQueue.addOperations(chain.operations, waitUntilFinished: false)
}
OperationsChain класс: Обертывания блок кода в Operation
и сохраняет работу в operations
массива поддерживающих зависимостей.
public class OperationsChain {
public private(set) var operations = [Operation]()
public init(blockExecutionQueue: DispatchQueue? = nil,
executionBlock: @escaping AsynchronousBlockOperation.WorkItemBlock) {
let op = AsynchronousBlockOperation(blockExecutionQueue: blockExecutionQueue, executionBlock: executionBlock)
operations.append(op)
}
public init(blockExecutionQueue: DispatchQueue? = nil,
executionBlock: @escaping SynchronousBlockOperation.WorkItemBlock) {
let op = SynchronousBlockOperation(blockExecutionQueue: blockExecutionQueue, executionBlock: executionBlock)
operations.append(op)
}
@discardableResult
public func thenAsync(blockExecutionQueue: DispatchQueue? = nil,
executionBlock: @escaping AsynchronousBlockOperation.WorkItemBlock) -> AsynchronousBlockOperation {
let op = AsynchronousBlockOperation(blockExecutionQueue: blockExecutionQueue, executionBlock: executionBlock)
if let lastOperation = operations.last {
op.addDependency(lastOperation)
} else {
assertionFailure()
}
operations.append(op)
return op
}
@discardableResult
public func thenSync(blockExecutionQueue: DispatchQueue? = nil,
executionBlock: @escaping SynchronousBlockOperation.WorkItemBlock) -> SynchronousBlockOperation {
let op = SynchronousBlockOperation(blockExecutionQueue: blockExecutionQueue, executionBlock: executionBlock)
if let lastOperation = operations.last {
op.addDependency(lastOperation)
} else {
assertionFailure()
}
operations.append(op)
return op
}
}
SynchronousBlockOperation и AsynchronousBlockOperation классы.
public final class SynchronousBlockOperation: Operation {
public typealias WorkItemBlock = (Void) -> Void
fileprivate var executionBlock: WorkItemBlock?
fileprivate var blockExecutionQueue: DispatchQueue?
public init(blockExecutionQueue: DispatchQueue? = nil, executionBlock: @escaping SynchronousBlockOperation.WorkItemBlock) {
self.blockExecutionQueue = blockExecutionQueue
self.executionBlock = executionBlock
super.init()
}
public override func main() {
if let queue = blockExecutionQueue {
queue.async { [weak self] in
self?.executionBlock?()
}
} else {
executionBlock?()
}
}
}
open class AsynchronousBlockOperation: AsynchronousOperation {
public typealias FinaliseBlock = (Void) -> Void
public typealias StatusBlock = (Void) -> Bool
public typealias WorkItemBlock = (@escaping StatusBlock, @escaping FinaliseBlock) -> Void
fileprivate var executionBlock: WorkItemBlock?
fileprivate var blockExecutionQueue: DispatchQueue?
public init(blockExecutionQueue: DispatchQueue? = nil, executionBlock: @escaping AsynchronousBlockOperation.WorkItemBlock) {
self.blockExecutionQueue = blockExecutionQueue
self.executionBlock = executionBlock
super.init()
}
open override func onStart() {
if let queue = blockExecutionQueue {
queue.async { [weak self] in
self?.executionBlock?({ return self?.isExecuting ?? false }) {
self?.finish()
}
}
} else {
executionBlock?({ [weak self] in return self?.isExecuting ?? false }) { [weak self] in
self?.finish()
}
}
}
}
AsynchronousOperation класс: многоразовый подкласс Operation
.
open class AsynchronousOperation: Operation {
fileprivate var lockOfProperties = NonRecursiveLock.makeDefaultLock()
fileprivate var lockOfHandlers = NonRecursiveLock.makeDefaultLock()
fileprivate var mFinished = false
fileprivate var mExecuting = false
}
extension AsynchronousOperation {
public final override var isAsynchronous: Bool {
return true
}
public final override var isExecuting: Bool {
return lockOfProperties.synchronized { mExecuting }
}
public final override var isFinished: Bool {
return lockOfProperties.synchronized { mFinished }
}
}
extension AsynchronousOperation {
public final override func start() {
if isCancelled || isFinished || isExecuting {
return
}
willChangeValue(forKey: "isExecuting")
lockOfProperties.synchronized { mExecuting = true }
onStart()
didChangeValue(forKey: "isExecuting")
}
public final override func cancel() {
super.cancel()
if isExecuting {
onCancel()
finish()
} else {
onCancel()
lockOfProperties.synchronized {
mExecuting = false
mFinished = true
}
}
}
public final func finish() {
willChangeValue(forKey: "isExecuting")
willChangeValue(forKey: "isFinished")
lockOfProperties.synchronized {
mExecuting = false
mFinished = true
}
onFinish()
didChangeValue(forKey: "isExecuting")
didChangeValue(forKey: "isFinished")
}
}
extension AsynchronousOperation {
/// Subclasses must launch job here.
///
/// **Note** called between willChangeValueForKey and didChangeValueForKey calls, but after property mExecuting is set.
open func onStart() {
}
/// Subclasses must cancel job here.
///
/// **Note** called immediately after calling super.cancel().
open func onCancel() {
}
/// Subclasses must release job here.
///
/// **Note** called between willChangeValueForKey and didChangeValueForKey calls,
/// but after properties mExecuting and mFinished are set.
open func onFinish() {
}
}
@ user2864740, перефразированный, я имел в виду стандартную библиотеку. –