openlang

Доступ к разрешённому результату за пределами его promise

Я работаю над своей системой аутентификации (да, той самой из моего предыдущего поста) и запутался в цепочках Promise.t (довольно типичная история для javascript).

Сейчас у меня вот такой код:

 fetch(...)
 ->Promise.then(Response.json)
 ->Promise.thenResolve(payload => {
   let record = JSON.Decode.object(payload)->Option.getOrThrow
   // let profile = Profile.fromJson(record)

   Console.log(record)
   // TODO: I should set the model to SignedInWithProfile(profile)
 })

Однако вариант SignedInWithProfile должен попасть во внешнюю область видимости, а не остаться внутри promise. Поскольку это reducer, трюки react мне недоступны, можно ли как-то решить эту проблему?

Может, написать какую-нибудь привязку javascript, чтобы это обойти? Я немного запутался в этом.

Переведено с английскогоПоказать оригинал

Разговор продолжается 3

Следуя доводам
  1. Ссылка на ответ

    Простейший подход: Сразу используйте await.

    Он работает и на верхнем уровне, и внутри тела async-функции.

    Документация: Async / Await | ReScript Language Manual (https://rescript-lang.org/docs/manual/async-await)

     let resolved: Profile.t = await fetch("")
     ->Promise.then(Response.json)
     ->Promise.thenResolve(payload => {
       let record = JSON.Decode.object(payload)->Option.getOrThrow
       Profile.fromJson(record)
     })

    Но вообще хорошая практика состоит в том, чтобы вызывать обновление состояния reducer через функцию dispatch:

     type model = {
       profile: option<Profile.t>,
     }
    
     type event = SetProfile(Profile.t)
    
     let reduceState = (model, event) => {
       switch event {
       | SetProfile(profile) => {
           ...model,
           profile: Some(profile),
         }
       }
     }
    
     @react.component
     let make = () => {
       let (model, sendAction) = React.useReducer(
         reduceState,
         {
           profile: None,
         },
       )
    
       React.useEffect(() => {
         fetch("")
         ->Promise.then(Response.json)
         ->Promise.thenResolve(payload => {
           let record = JSON.Decode.object(payload)->Option.getOrThrow
           let profile = Profile.fromJson(record)
    
           sendAction(SetProfile(profile))
         })
         ->Promise.ignore
    
         None
       }, [sendAction])
    
       switch model.profile {
       | Some(profile) => <div> {React.string("Profile ready")} </div>
       | None => <div> {React.string("Waiting...")} </div>
       }
     }
    Переведено с английскогоПоказать оригинал
  2. Ссылка на ответ

    Я нахожусь внутри reducer, поэтому async-контекста, в котором можно использовать await, у меня нет.

    С dispatch внутри promise в моём случае всё несколько сложнее. Возможно, стоит сделать всё async по умолчанию и расставить promises по всему коду, не знаю.

    Я вообще плохо понимаю, как изначально организовать свой код. Наверное, мне просто нужно чуть больше опыта в этом.

    Переведено с английскогоПоказать оригинал
  3. Ссылка на ответ

    Вообще говоря, стоит избегать запуска побочных эффектов внутри reducer. Именно поэтому reducers обычно не содержат async-функций, и так и должно оставаться.

    Когда action меняет состояние reducer, можно сделать это состояние зависимостью чего-то вроде useEffect, а свои (async) побочные эффекты, включая вызовы fetch, выполнять там.

    Также полезно явно перечислить возможные состояния компонента:

     type model = Pending | Ready(Profile.t)
    
     type event = ReplaceState(model)
    
     let reduceState = (model, event) =>
       switch event {
       | ReplaceState(model) => model
       }
    
     @react.component
     let make = () => {
       let (model, sendAction) = React.useReducer(reduceState, Pending)
    
       React.useEffect(() => {
         fetch("")
         ->Promise.then(Response.json)
         ->Promise.thenResolve(payload => {
           let record = JSON.Decode.object(payload)->Option.getOrThrow
           let profile = Profile.fromJson(record)
    
           sendAction(ReplaceState(Ready(profile)))
         })
         ->Promise.ignore
    
         None
       }, [sendAction]
    
       switch model {
       | Ready(profile) => <div> {React.string("Profile ready")} </div>
       | Pending => <div> {React.string("Waiting...")} </div>
       }
     }

    При этом я сам часто прибегаю к приёму, который принудительно создаёт async-контекст, когда он нужен:

     module AsyncRunner = {
       let run = (createTask: unit => promise<unit>) =>
         createTask()->Promise.ignore
     }
    
     let handleSync = () =>
       AsyncRunner.run(async () => {
         let payload = await fetch("")->Promise.then(Response.json)
         let record = JSON.Decode.object(payload)->Option.getOrThrow
         let profile = Profile.fromJson(record)
    
         sendAction(ReplaceState(Ready(profile)))
       })
    Переведено с английскогоПоказать оригинал

Другие стороны того же вопроса

  1. Как в Java Mail показывать текстовое имя отправителя вместо его настоящего адреса электронной почты

    Я отправляю сообщения через Java Mail API. После доставки во входящих отображается адрес отправителя. Хочу показывать вместо…

    Ответы 2Последняя активность java
  2. Доступ к Dynamics CRM из TypeScript

    Мой код CRM выполняется в веб-ресурсе, поэтому для получения данных сущности приходится писать чтобы обратиться к этим данным…

    Ответы 2Последняя активность typescript
  3. Какой формат даты нужен заголовку HTTP-ответа в Gleam?

    Я хочу отправлять заголовок Date. Требуемый формат выглядит следующим образом: Вот мой текущий вариант кода:

    Ответы 3Последняя активность gleam