Náufrago


Náufrago

bottle

Há um náufrago assíncrono neste oceano, ele só consegue ler e escrever mensagens através de uma garrafa. Ele não quer ser salvo, mas fica poluindo o mar com garrafas plásticas não biodegradáveis.

Para o fazer parar é necessário responder 5 garrafas todas em menos de 50 milissegundos, assim ele acha que estão chegando perto.

Garrafa

const mkBottle = pivo => function bottle (a) {
    if (a) {
        pivo = a
    } else {
        return pivo
    }
}

const bottle = mkBottle()

bottle({
    message: "tem alguém ai?"
})
bottle()
bottle({
    message: "sim"
})
bottle()

Alarme

const stripromise = function stripromise () {
    let resolver
    let rejecter
    return {
        promise: new Promise((a, b) => {
            resolver = a
            rejecter = b
        }),
        resolver,
        rejecter
    }
}

let {promise: p, resolver: r, rejecter: j} = stripromise()
p.then(console.log).catch(console.error)

r('opa')

Como saber se a garrafa foi respondida?

let {promise, resolver} = stripromise()
bottle({
    message: "tem alguém ai",
    bipMeAt: resolver
})

promise.then(console.log)

bottle().bipMeAt("alô")

Aperte F12

Há uma variável global bottle que é uma função, quando você executa sem argumentos ela retorna o que tem dentro da garrafa. Com argumento, salva ele dentro da garrafa.

O náufrago vai colocar na garrafa um objeto que possui um atributo bipMeAt, que ao executar faz com que ele escreva uma nova mensagem na garrafa. Esta função é o resolve de uma promessa, para ficar mais fácil existe a stripromise que vira uma nova Promise do avesso, você recebe um objeto contendo a promessa e suas funções de disparo destacadas.

Condição de disputa

Por que o código a seguir não resolve?

bottle().bipMeAt()
bottle().bipMeAt()
bottle().bipMeAt()
bottle().bipMeAt()
bottle().bipMeAt()
bottle().bipMeAt()
bottle().bipMeAt()
bottle().bipMeAt()

Porque ele executa várias vezes a mesma função bipMeAt. São a mesma mensagem na garrafa porque não acabou a fila de tarefas pra começar a fila de promessas, que é quando o naufrago escreve novas mensagens. Você executou o resolve da promessa mas ela não chaveia imediatamente.

Se você executar cada linha no console vai perceber que dá tempo pro náufrago colocar outra bipMeAt, mas certamente você não consegue repetir isso 6 vezes em menos de 50ms.

Há várias maneiras de contornar isso. Uma delas é colocar cada bottle().bipMeAt depois de setTimeout

setTimeout(() => {
  bottle().bipMeAt()
  setTimeout(() => {
    bottle().bipMeAt()
    setTimeout(() => {
      bottle().bipMeAt()
      setTimeout(() => {
        bottle().bipMeAt()
        setTimeout(() => {
          bottle().bipMeAt()
          setTimeout(() => {
            bottle().bipMeAt()
            }, 0)
          }, 0)
        }, 0)
      }, 0)
    }, 0)
  }, 0)

Assim você está passando a bola pra fila das promessas. Contudo neste código você não espera pelo náufrago chamar de volta pelo bipMeAt da garrafa, algo que ele faz quando termina de responder uma mensagem.

let bipAfter = bottle().bipMeAt

bottle({
  bipMeAt: () => {
    console.log("wilson, é você?")
  }
})

bipAfter()

Se ele demorasse pra responder, o setTimeout pra 0 não iria funcionar. Pior, temporarimamente pode ter alguma função bomba no bipMeAt.

No arquivo resposta tem uma solução que usa promessas recursivas, seria uma forma contraída desda solução:

Promise.resolve().then(() => {
  const {promise, resolver: bipMeAt} = stripromise()
  const bipAfter = bottle().bipMeAt
  bottle({bipMeAt})
  bipAfter()
  return promise
}).then(() => {
  const {promise, resolver: bipMeAt} = stripromise()
  const bipAfter = bottle().bipMeAt
  bottle({bipMeAt})
  bipAfter()
  return promise
}).then(() => {
  const {promise, resolver: bipMeAt} = stripromise()
  const bipAfter = bottle().bipMeAt
  bottle({bipMeAt})
  bipAfter()
  return promise
}).then(() => {
  const {promise, resolver: bipMeAt} = stripromise()
  const bipAfter = bottle().bipMeAt
  bottle({bipMeAt})
  bipAfter()
  return promise
}).then(() => {
  const {promise, resolver: bipMeAt} = stripromise()
  const bipAfter = bottle().bipMeAt
  bottle({bipMeAt})
  bipAfter()
  return promise
}).then(() => {
  const {promise, resolver: bipMeAt} = stripromise()
  const bipAfter = bottle().bipMeAt
  bottle({bipMeAt})
  bipAfter()
  return promise
})