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)
     })

    다만 일반적으로는 dispatch 함수를 통해 reducer의 상태 업데이트를 일으키는 것이 좋은 방법이에요:

     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 안에 있어서 await를 쓸 수 있는 async 컨텍스트가 없어요.

    제 경우에는 promise 안에서 dispatch하는 게 좀 더 복잡해요. 그냥 기본적으로 전부 async로 만들고 코드 곳곳에 promise를 넣어야 할지도 모르겠네요.

    애초에 코드 구조를 어떻게 잡아야 할지부터 막막해요. 그냥 경험이 좀 더 필요한 것 같아요.

    영어에서 번역원문 보기
  3. 고유 링크

    일반적으로 reducer 안에서 부수 효과를 시작하는 건 피하는 게 좋아요. 그래서 reducer에는 보통 async 함수가 없고, 그렇게 유지해야 해요.

    action이 reducer 상태를 바꾸면, 그 상태를 useEffect 같은 것의 의존성으로 넣고 그 안에서 fetch 호출 같은 (async) 부수 효과를 수행할 수 있어요.

    컴포넌트가 가질 수 있는 상태를 명시적으로 나열하는 것도 도움이 돼요:

     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로 메시지를 보내고 있어요. 도착한 메일의 받은편지함에는 보낸 주소가 표시돼요. 그 발신자 주소 대신 텍스트를 보여 주고 싶어요. facebook에서 오는 메일은 받은편지함에 "mailbox@faceb…

    답글 2최근 활동 java
  2. TypeScript에서 Dynamics CRM에 접근하기

    CRM 코드가 웹 리소스 안에서 실행되다 보니 엔터티 데이터를 가져오려면 이렇게 작성해야 해요. 그런데 typescript로 작성하면 이 표현식이 컴파일되지 않아요. 지금은 Xrm.Page 앞에 window.parent를..…

    답글 2최근 활동 typescript
  3. Gleam HTTP 응답 헤더에는 어떤 날짜 형식이 필요한가요?

    Date 헤더를 보내고 싶어요. 필요한 형식은 다음과 같아요: 지금까지 시도한 코드는 이거예요:

    답글 3최근 활동 gleam