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 邮件在收件箱里显示 "Facebook - Message title",而不是 "mail…

    回复 2最近活动 java
  2. 从 TypeScript 访问 Dynamics CRM

    我的 CRM 代码在网页资源中运行,所以要获取实体数据,就得写成 这样来读取数据。 可惜用 typescript 编写时,这个表达式无法编译。现在我手动把 window.parent 加到 Xrm.Page 前面……(Xrm 类型定义已经装好了),这…

    回复 2最近活动 typescript
  3. Gleam 的 HTTP 响应头需要怎样的日期格式?

    我想发送一个 Date 响应头。所需的格式如下。 这是我目前尝试的代码。

    回复 3最近活动 gleam