【问题标题】:How to make a Javascript/React/Typescript fetch call asynchronous?如何使 Javascript/React/Typescript 获取调用异步?
【发布时间】:2020-03-25 08:14:27
【问题描述】:

考虑以下 Javascript/React 代码:

// Javascript function that has a fetch call in it. 
export const signIn = (email:string, password:string) => {
  console.log("FETCHING...");

  fetch(`${endPoint}/sign_in`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      email,
      password
    })
  })
  .then((response) => {
    return response.json()
  })
  .then(({ data }) => {
    console.log("FETCHED DATA...")
  })
  .catch((error) => {
    console.error('ERROR: ', error)
  })

  console.log("DONE FETCHING...");
}

// A functional component that references signIn.
export const SignIn: React.FC<Props> = () => {
  // irrelevant code ...

  const onSubmit = (e: CustomFormEvent) => {
    e.preventDefault()
    console.log("SIGNING IN...")
    // calls my signIn function from above
    // I don't want this to finish until the fetch inside it does.
    signIn(email, password, setAuthentication, setCurrentUser)
    console.log("SIGNED IN...");
  }

  return <>A form here submits and calls onSubmit</>
}

这会产生以下控制台日志输出:

SIGNING IN...
FETCHING...
DONE FETCHING...
SIGNED IN...
FETCHED DATA...

我希望 FETCHED DATA... 出现在 DONE FETCHING... 之前。我试过玩 aysnc/await 但这不起作用,所以我不知道从哪里开始。

【问题讨论】:

    标签: javascript reactjs async-await fetch-api


    【解决方案1】:

    您可能想进一步了解 JavaScript 中的 Promise 是如何工作的。

    这里有一个问题是signIn。你现在正在做的是:

    function signIn() {
      // 1. log FETCHING
      // 2. call asynchronous fetch function
      // 3. log DONE FETCHING
    }
    

    这里的关键是fetch异步的。该程序在继续之前不会等待它完成。看到问题了吗? JavaScript 解释器将运行第 3 步,而无需等待第 2 步完成。

    有多种方法可以解决此问题。首先,您可以使用then。这是一个例子:

    promise
      .then(res => func1(res))
      .then(res => func2(res))
      .then(res => func3(res))
    

    在这里,你告诉 JavaScript:

    1. 运行promise,并等待它解决
    2. promise 获取结果并将其传递给func1。等待func1 解决。
    3. func1 获取结果并将其传递给func2。等待func2 解决。

    这里的关键区别在于您按顺序运行每个then 块,等待之前的每个promise 得到解决,然后再执行下一个promise。 (而在您没有等待承诺解决之前)。

    您的带有承诺的代码如下所示:

    export const signIn = (email: string, password: string) => {
      console.log("FETCHING...")
      // Note that we return the promise here. You will need this to get onSubmit working.
      return fetch(/* args */)
        .then(res => res.json())
        .then(({ data }) => console.log("DONE FETCHING"))
        .catch(err => /* HANDLE ERROR */)
    }
    

    解决此问题的第二种方法是使用asyncawaitasyncawait 只是对 Promise 的语法糖。它在下面的作用是完全相同的,因此请确保您首先了解 Promise 是如何工作的。这是带有asyncawait 的代码:

    // The async keyword here is important (you need it for await)
    export const signIn = async (email: string, password: string) => {
      console.log("FETCHING...");
    
      try {
        const res = await fetch(/* args */) // WAIT for fetch to finish
        const { data } = res.json()
        console.log("FETCHED DATA...")
      } catch (err) {
        /* HANDLE ERROR */
      }
    
      console.log("DONE FETCHING...")
    }
    

    onSubmit 中还有第二个类似的问题。想法是一样的;我会让你自己弄清楚(重要的是你必须从signIn返回一个Promise)。

    【讨论】:

    • 我怎样才能做到这一点,这样我就不必从signIn 传回承诺,而是一种signIn 有效载荷?我可以使用另一个Promise 将我正在做的事情包装在signIn 中并使用键入的resolve/reject 吗?如果我的要求没有意义,请告诉我。
    • 如果你想从signIn返回一些有效载荷,只需将有效载荷包装在一个承诺中。例如,您可以在signIn 的末尾执行类似return fetch(/* ... */).then(/* ... */).then(/* ... */).then(() =&gt; ({ some: 'payload' })) 的操作。那么payload就是解析signIn的返回值后得到的。
    【解决方案2】:

    为了使用异步等待,您需要从调用中返回一个承诺。所以基本上你不执行 .then 并将调用包装在 try catch 块中。

    export const signIn = async (email:string, password:string) => {
      console.log("FETCHING...");
    
      return fetch(`${endPoint}/sign_in`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          email,
          password
        })
      })
    }
    

      const onSubmit = async (e: CustomFormEvent) => {
        e.preventDefault()
        console.log("SIGNING IN...")
        // calls my signIn function from above
        // I don't want this to finish until the fetch inside it does.
        try {
            const data = await signIn(email, password, setAuthentication, setCurrentUser)
            // Parse data, do something with it. 
            console.log("SIGNED IN...");
        } catch (e) {
            // handle exception 
        }
      }
    

    【讨论】:

      【解决方案3】:

      如果您希望console.log 等到promise 得到解决,它必须在then 语句中。这是一个使用async/await的示例:

      export const signIn = async (email:string, password:string) => {
        console.log("FETCHING...");
      
        const response = await fetch(`${endPoint}/sign_in`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            email,
            password
          })
        })
      
        const data = await response.json();
      
        console.log("FETCHED DATA...")
        console.log("DONE FETCHING...");
      }
      

      如果您希望 console.log 在数据提取完成后发生,您还需要将其转换为 async 函数:

        const onSubmit = async (e: CustomFormEvent) => {
          e.preventDefault()
          console.log("SIGNING IN...")
          // calls my signIn function from above
          // I don't want this to finish until the fetch inside it does.
          await signIn(email, password, setAuthentication, setCurrentUser)
          console.log("SIGNED IN...");
        }
      

      【讨论】:

        【解决方案4】:

        只需添加另一个 .then

          .then((response) => {
            return response.json()
          })
          .then(({ data }) => {
            console.log("FETCHED DATA...")
            return
          }).then(()=> {
              console.log("DONE FETCHING...");
          })
          .catch((error) => {
            console.error('ERROR: ', error)
          })
        

        【讨论】:

          猜你喜欢
          • 2019-12-05
          • 1970-01-01
          • 2023-01-31
          • 1970-01-01
          • 2019-11-05
          • 2015-02-19
          • 1970-01-01
          • 2019-08-02
          • 1970-01-01
          相关资源
          最近更新 更多