如何使 Javascript/React/Typescript 获取调用异步?
Posted
技术标签:
【中文标题】如何使 Javascript/React/Typescript 获取调用异步?【英文标题】:How to make a Javascript/React/Typescript fetch call asynchronous? 【发布时间】: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
但这不起作用,所以我不知道从哪里开始。
【问题讨论】:
【参考方案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:
-
运行
promise
,并等待它解决。
从promise
获取结果并将其传递给func1
。等待func1
解决。
从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 */)
解决此问题的第二种方法是使用async
和await
。 async
和 await
只是对 Promise 的语法糖。它在下面的作用是完全相同的,因此请确保您首先了解 Promise 是如何工作的。这是带有async
和await
的代码:
// 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(() => ( 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)
)
【讨论】:
以上是关于如何使 Javascript/React/Typescript 获取调用异步?的主要内容,如果未能解决你的问题,请参考以下文章