ios - 在后台线程中执行组合 future 不起作用

标签 ios grand-central-dispatch future combine

如果你在 Playground 上运行它:

import Combine
import Foundation

struct User {
    let name: String
}

var didAlreadyImportUsers = false

var importUsers: Future<Bool, Never> {
    Future { promise in
        sleep(5)
        promise(.success(true))
    }
}

var fetchUsers: Future<[User], Error> {
    Future { promise in
        promise(.success([User(name: "John"), User(name: "Jack")]))
    }
}

var users: AnyPublisher<[User], Error> {
    if didAlreadyImportUsers {
        return fetchUsers
            .receive(on: DispatchQueue.global(qos: .userInitiated))
            .eraseToAnyPublisher()
    } else {
        return importUsers
            .receive(on: DispatchQueue.global(qos: .userInitiated))
            .setFailureType(to: Error.self)
            .combineLatest(fetchUsers)
            .map { $0.1 }
        .eraseToAnyPublisher()

    }
}

users
    .receive(on: DispatchQueue.global(qos: .userInitiated))
    .sink(receiveCompletion: { completion in
    print(completion)
}, receiveValue: { value in
    print(value)
})

print("run")


输出将是:
[User(name: "John"), User(name: "Jack")]
run
finished

但我期待得到:
run
[User(name: "John"), User(name: "Jack")]
finished

因为接收器应该在后台线程中运行代码。我在这里缺少什么。
我是否需要 rin 代码:
 sleep(5)
 promise(.success(true))

在后台线程中?那么这样做的目的是什么
.receive(on: DispatchQueue.global(qos: .userInitiated))

最佳答案

您的 Future 在创建后立即运行,因此在您的情况下,只要访问此属性:

var importUsers: Future<Bool, Never> {
  Future { promise in
    sleep(5)
    promise(.success(true))

  }
}

自从Future立即运行,这意味着传递给 Promise 的闭包立即执行,使主线程休眠 5 秒,然后继续运行。在你的情况下 Future访问 users 后立即创建这是在主线程上完成的。
receive(on:影响 sink 的线程(或下游发布者)接收值,而不是创建它们的位置。由于 future 在您调用 .sink 时已经完成,完成和发出的值被传递到 sink立即地。在后台队列上,但仍然立即。

在那之后,你终于点击了print("run")线。

如果您更换 sleep(5)有点这个:
var importUsers: Future<Bool, Never> {
  Future { promise in
    DispatchQueue.global().asyncAfter(deadline: .now() + 5) {
      promise(.success(true))
    }
  }
}

并对您的订阅代码进行一些小的调整:
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

var cancellables = Set<AnyCancellable>()

users
  .receive(on: DispatchQueue.global(qos: .userInitiated))
  .sink(receiveCompletion: { completion in
    print(completion)
  }, receiveValue: { value in
    print(value)
  }).store(in: &cancellables)

您将看到输出按预期打印,因为最初的 future 不会阻塞主线程五秒钟。

或者,如果您保持 sleep 并像这样订阅,您将看到相同的输出:
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

var cancellables = Set<AnyCancellable>()

users
  .subscribe(on: DispatchQueue.global(qos: .userInitiated))
  .sink(receiveCompletion: { completion in
    print(completion)
  }, receiveValue: { value in
    print(value)
  }).store(in: &cancellables)

原因是你subscribe在后台线程上,因此订阅和所有内容都是在主线程之外异步设置的,这会导致 print("run")在收到 Future 之前运行的结果。然而,当 users属性被访问(在主线程上),因为那是你初始化 Future 的时候。 .所以整个输出是一次性打印出来的,而不是在 "run" 之后休眠 5 秒。 .

关于ios - 在后台线程中执行组合 future 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62264708/

相关文章:

ios - TableView 的问题和音频 AVplayer 的使用

java - 应用程序中的 webview 是否缓存页面?

ios - 使用 dispatch_sync 在 uicollectionview 上延迟加载

objective-c - 使用信号量阻塞很多,然后释放所有

Java 线程 - 奇怪的 Thread.interrupted() 和 future.cancel(true) 行为

scala - Akka : the proper use of `ask` pattern?

ios - 如何声明自定义对象,例如。可编码类中的计时器

ios - UIApplicationWillEnterForegroundNotification与application:openURL:sourceApplication:annotation:

ios - 我如何在 GCD 中将一个 block 添加到调度队列的前面?

java - 使用 Akka 远程系统玩 2.0.2