Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

220
Views
Emitir valor en intervalos aleatorios si se aplica la condición

Aquí están los tres puntos que quiero lograr:

  1. Quiero emitir un valor específico en un intervalo aleatorio
  2. Quiero emitir solo si aguas arriba tiene un valor específico
  3. Quiero cancelar el intervalo de tiempo aleatorio y reiniciarlo si hay un nuevo valor aguas arriba

Para ponerlo más en contexto:

Quiero alternar un estado en el grifo de A a B, B a C y luego volver de C a A. Sin embargo, si el estado actual es B y no hay entrada del usuario, quiero cambiar el estado a C después de un tiempo aleatorio intervalo. Si el estado actual es B y el usuario toca la pantalla, no quiero volver a cambiar el estado a C, con la excepción de que el usuario cambie con la frecuencia suficiente para volver a estar en B, en cuyo caso querría un nuevo intervalo aleatorio para ser utilizado.

Tengo un pseudo código (más o menos) para aclarar aún más mi problema.

 class ViewModel { var state: State private var stateSubject: CurrentValueSubject<State, Never> private var cancellables: Set<AnyCancellable> = [] init(initialState: State) { state = initialState stateSubject = CurrentValueSubject(state) // Pipe to store new state stateSubject .sink { [unowned self] in self.state = $0 } .store(in: &cancellables) // Logic to switch specific state after interval // Approach 1 stateSubject .debounce(for: .seconds(randomInterval), scheduler: RunLoop.main) .filter { $0 == .B } .sink { [unowned self] _ in stateSubject.send(.C) } .store(in: &cancellables) // -------------------------------- // Approach 2 stateSubject .flatMap(maxPublishers: .max(1)) { state in Future { [unowned self] promise in DispatchQueue.main.asyncAfter(deadline: .now() + randomInterval) { promise(.success(state)) } } } .filter { $0 == .B } .sink { [unowned self] _ in stateSubject.send(.C) } .store(in: &cancellables) } var randomInterval: Double { Double.random(in: 1...4) } // Called on tap func toggleState() { state = state.toggle() } } extension ViewModel { enum State { case A, B, C func toggle() -> Self { switch self { case .A: return .B case .B: return .C case .C: return .A } } } }

Enfoque 1: Sin embargo, esto parece razonable, ya que el operador .debounce solo se crea una vez y, por lo tanto, solo accede a randomInterval una vez, lo que da como resultado que el rebote siempre sea la misma cantidad de tiempo.

Enfoque 2: esto funciona bastante bien, excepto si el usuario cambia el estado con demasiada frecuencia. No pude encontrar la causa del problema, pero encontré que el estado alternaba incluso en State.A

Gracias por su ayuda y avíseme si tiene más preguntas.

over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

¡Desafío aceptado! :)

 import Foundation import Combine // for the example sake, // pretending that the user taps exactly every 3 seconds // use the real UI taps publisher here // it emits an integer "tap ID", you can use a tap timestamp here instead, // it just has to be unique for each tap let taps = (1...).publisher.flatMap(maxPublishers: .max(1)) { Just($0).delay(for: .seconds(3), scheduler: RunLoop.main) } // each tap timeouts emitting its tap ID after a random delay // except the first one which fires immediately with the tap let timeouts = taps.map { Just($0).delay(for: .seconds(($0 > 1) ? Double.random(in: 1...4) : 0), scheduler: RunLoop.main) }.switchToLatest() // when a new tap arrives, cancel the previous timeout // using 0, 1, 2 here instead of A, B, C for simplicity func next(_ state: Int) -> Int { (state + 1) % 3 } // given the previous state and what happened - returns the new state func update(_ prevState: Int, _ event: (Int, Int)) -> Int { let (tapID, timeoutTapID) = event let isTimeout = (tapID == timeoutTapID) let isTap = !isTimeout if isTap { print("event: tap \(tapID)") } else { print("event: timeout \(timeoutTapID)") } if isTap || (prevState == 1) { return next(prevState) } else { // ignore timeouts in other states return prevState } } let initialState = 0 let states = taps.combineLatest(timeouts) .scan(initialState, update) .removeDuplicates() let sub = states.sink { value in print("new state: \(value)") }

Manifestación:

 event: timeout 1 new state: 0 event: tap 2 new state: 1 event: timeout 2 new state: 2 event: tap 3 new state: 0 event: timeout 3 event: tap 4 new state: 1 event: timeout 4 new state: 2 event: tap 5 new state: 0 event: tap 6 new state: 1 event: tap 7 new state: 2 event: timeout 7 event: tap 8 new state: 0 event: tap 9 new state: 1 event: tap 10 new state: 2 event: timeout 10 event: tap 11 new state: 0 event: tap 12 new state: 1 event: tap 13 new state: 2 event: timeout 13

Una desventaja es que siempre se está ejecutando un temporizador de tiempo de espera, incluso cuando el estado actual no lo necesita (ignoramos tales actualizaciones). Esperemos que sea solo un temporizador en todo momento.

Hacer que la disponibilidad del temporizador dependa del estado requiere un ciclo de retroalimentación que traté de evitar.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!