如何使Javascript / React / Typescript提取调用异步?

时间:2019-11-29 18:47:51

标签: javascript reactjs async-await fetch-api

考虑以下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,但是那没有用,所以我不知道从这里去哪里。

4 个答案:

答案 0 :(得分:3)

只需添加另一个.then

#include <iostream>
#include <limits>
#include <cstdlib>

class seconds
{
protected:
    int secs;

public:
    seconds()
    {
        secs = 0;
    }

    void getsecs()
    {
        std::cout << "\n enter the time in seconds:"; 
        std::cin >> secs;
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
};

class minutes : public seconds
{
protected:
    int mins;

public:
    minutes()
    {
        mins = 0;
    }

    void convert()
    {
        getsecs();
        mins = secs / 60;
        secs %= 60;
    }
};

class hours : public minutes
{
protected:
    int hrs;

public:
    hours()
    {
        hrs = 0;
    }

    void convert1()
    {
        convert();
        hrs = mins / 60;
        mins %= 60;
    }

    void show()
    {
        std::cout << "hrs:" << hrs << "\t mins" << mins << "\t secs" << secs;
    } 
};

int main()
{
    std::system("clr");

    hours h;
    h.convert1();
    h.show();

    std::cin.get();
    return 0;
}

答案 1 :(得分:3)

如果您想让then等到承诺被兑现,则必须在console.log语句中。这是一个使用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...");
  }

答案 2 :(得分:3)

为了使用异步等待,您需要从调用中返回一个承诺。因此,基本上,您无需执行.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 :(得分:2)

您可能想更多地了解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块,等待每个先前的承诺得到解决,然后再转到下一个。 (而在您不等待诺言解决之前)。

带有承诺的代码如下:

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只是对诺言的语法糖。它的基本功能是完全相同的,因此请确保您首先了解承诺的工作方式。这是带有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中还有另一个类似的问题。想法是一样的。我会让你自己弄清楚(重要的是必须从Promise返回signIn)。