如何将 .then 和 .catch 转换为 async/await

4

我正在使用React/Redux前端和自己的nodejs/express api开发登录表单/注销按钮。但是在处理登录表单时遇到了问题。大部分情况下它能正常工作,但是经常会出现错误。第一个错误是forbidden,这告诉我在发送userDetails请求之前用户没有通过身份验证。

还有另一个问题,Redux没有更改用户角色,我需要动态呈现导航菜单。我认为将handleLogin转换为async/await可能是解决方案,但我觉得我做得不对。

import React from 'react';
import { login, userDetails } from '../axios/homeApi';
import { useForm } from 'react-hook-form';
import { useDispatch } from 'react-redux';
import { setLogin, setRole } from '../redux/actions';
const LoginForm = () => {
const { handleSubmit, register, errors } = useForm();
const dispatch = useDispatch();

const handleLogin = values => {
    login(values.email, values.password)
    .then(res => {
        const token = res.data.token;
        window.localStorage.setItem('auth', token);
        dispatch(setLogin({ loggedIn: true }));
        userDetails()
        .then(res => {
            const role = res.data.data.role;
            dispatch (setRole({ role }));
        })
    })
}

return (
    <div>
        <form action="" onSubmit={handleSubmit(handleLogin)} className="footer-form">
            <input
                type="email"
                placeholder="Enter Email Here"
                name="email"
                ref={register({ required: "Required Field" })}
            />
            <input
                type="password"
                placeholder="Enter Password Here"
                name="password"
                ref={register({
                    required: "Required Field",
                    minLength: { value: 6, message: "Minimum Length: 6 Characters" }
                })}
            />
            {errors.password && errors.password.message}
            {errors.email && errors.email.message}
            <input type="submit" value="Login" />
        </form>
    </div>
)
}

export default LoginForm;

这是我尝试将handleLogin转换为async/await的最佳方法。我正在尝试理解如何从这些调用中获取数据。
const handleLogin = async values => {
    try {
        const {data: {token}} = await login(values.email, values.password)
        window.localStorage.setItem('auth', token);
        console.log(token);
        const user = await userDetails();
        await dispatch(setLogin({ loggedIn: true}))
        await dispatch(setRole(user.data.data.role))
    } catch(err) {
        console.log(err)
    }
}

非常感谢您提供的任何帮助和指导。


dispatch 不可等待。 - Dai
你应该考虑转换到TypeScript,因为了解类型(“形状”)以及知道什么是可等待的和什么不是,可以使编写现代JavaScript变得更加容易。 - Dai
3个回答

5

当使用await时,您需要考虑到变量值是与没有await返回到res的值相同的。

因此,如果您有以下代码:

login(values.email, values.password)
    .then(res => { 
     })

这就像是:

var login = await login(values.email, values.password);

所以按照这种逻辑,这样:

login(values.email, values.password)
    .then(res => {
        const token = res.data.token;
        // whatever
        userDetails()
        .then(res => {
            const role = res.data.data.role;
            // whatever
        })
    })

请翻译成中文:

var login = await login(values.email, values.password)
const token = login.data.token;
// do whatever
var userDetails = await userDetails()
const role = userDetails.data.data.role;
// whatever

查看此示例的工作方式。代码是“相同”的。一个使用.then,另一个使用await

runThenFunction();

runAwaitFunction();

function runThenFunction(){
  console.log("Enter then function")
  this.returnPromise(2000).then(res => {
    console.log(res);
    this.returnPromise(1000).then(res => {
      console.log(res);
    });
  });
  //You can log here and it will be displayed before the promise has been resolved
  console.log("Exit then function")
}

async function runAwaitFunction(){
  console.log("Enter await function")
  var firstStop = await returnPromise(1000);
  console.log(firstStop)
  var secondStop = await returnPromise(4000);
  console.log(secondStop)
  // Using await the code "stops" until promise is resolved
  console.log("Exit await function")
}

function returnPromise(time){
  return new Promise(resolve => setTimeout(() => resolve("hello: "+time+"ms later."), time));
}


仍然收到未授权错误。检查了瀑布流,和等待是按预期工作的。但我没有及时地获得来自登录的所需信息。设置一个超时可能是正确的方法,因为登录调用平均需要4毫秒,但我很好奇这是否对于生产质量的应用程序是个好的做法。 - Aaron Toliver
1
@AaronToliver我建议你尽力避免超时!最佳实践是等待响应完成,而不是等待任意时间持续。 - Gershom Maes
为什么在 runThen 中使用 this 来返回 returnPromise?你可以不用它。 - Timo

4

1
考虑任何具有then属性的对象,该属性是一个函数,它接受一个回调作为其第一个参数,例如:
let obj = {
  then: callback => callback('hello')
};

await将任何这样的对象转换为then提供给回调函数的值。因此:

(await obj) === 'hello'

在您的示例中,有两个实例需要将值返回给then回调函数:
login(...).then(res => { /* got res */ });

并且

userDetails().then(res => { /* got res */ });

await 简单地看作是一种获取对象返回值的方式,这些对象是由 login(...)userDetails() 返回的结果,在这种情况下,你可以把它转换为一个对象的 then 回调函数!

let res = await login(...);

并且

let res = await userDetails();

你可以看到,这也节省了大量的缩进,这是人们喜欢使用async/await的众多原因之一!
当将这些从then回调值转换为await时,插入到您的代码中,看起来像这样:
const handleLogin = async values => {
  
  let loginRes = await login(values.email, values.password);
  let token = loginRes.data.token;
  window.localStorage.setItem('auth', token);
  dispatch(setLogin({ loggedIn: true }));
  
  let userDetailsRes = await userDetails();
  let role = userDetailsRes.data.data.role;
  dispatch(setRole({ role }));
  
};

请注意,该函数必须标记为async,而且我已经将res重命名为更具体的名称,因为两个响应现在存在于完全相同的范围内,并且需要彼此区分。
总的来说,每当您使用then在回调中获取某个值时,您都可以转换为更优雅的await等效形式。

这几乎是我所做的一模一样。仍然无法获取userDetails()的forbidden错误。 const handleLogin = async values => { let signin = await login(values.email, values.password); const token = signin.data.token; window.localStorage.setItem('auth', token);dispatch(setLogin({ loggedIn: true })); const user = await userDetails(); console.log(user.data.role); }我有另一个解决方案,但它不涉及到这个。这太棒了。我将使用这些知识重构这个应用程序。 - Aaron Toliver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接