在后台线程中执行合并将来不起作用

问题描述 投票:0回答:1

如果您在操场上运行它:

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

因为接收器应在后台线程中运行代码。我在这里想念的。我是否需要冲洗代码:

 sleep(5)
 promise(.success(true))

在后台线程中?那么

的目的是什么
.receive(on: DispatchQueue.global(qos: .userInitiated))
ios grand-central-dispatch future combine
1个回答
0
投票

您的Future会在创建后立即运行,因此在您使用的情况下,一旦访问此属性,便会立即运行:

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

  }
}

并且[Future由于立即运行,这意味着传递给promise的闭包将立即执行,使主线程在继续运行之前先休眠5秒钟。在您的情况下,一旦您访问在主线程上完成的Future,就会立即创建users

receive(on:影响sink在其上接收值的线程,而不影响在其上创建值的线程。由于在您调用.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)

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

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

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属性(位于主线程上),主线程仍会睡5秒钟,因为那是在初始化Future时。

© www.soinside.com 2019 - 2024. All rights reserved.