Javascript 如何使React-Redux异步操作返回承诺?

Javascript 如何使React-Redux异步操作返回承诺?,javascript,reactjs,redux,es6-promise,redux-thunk,Javascript,Reactjs,Redux,Es6 Promise,Redux Thunk,我有一个redux操作函数,它返回一个承诺。在该函数体中,还有另一个异步函数,它在完成调用时返回回调。然后我在另一个地方调用这个函数,并用链接它。然后(),但是当在浏览器中使用断点调试它时,promise函数在前两行代码之后存在 return new Promise((resolve, reject) => { return (dispatch, getState) => { 是因为第二个return语句吗?这是react/redux代码,因此我必须为异步Thunk-red

我有一个redux操作函数,它返回一个承诺。在该函数体中,还有另一个异步函数,它在完成调用时返回回调。然后我在另一个地方调用这个函数,并用
链接它。然后()
,但是当在浏览器中使用断点调试它时,promise函数在前两行代码之后存在

return new Promise((resolve, reject) => {
    return (dispatch, getState) => {
是因为第二个return语句吗?这是
react/redux
代码,因此我必须为异步Thunk-redux操作提供第二条返回语句。我怎样才能做到这一点?这样我就可以在另一个redux操作中调用它,并将它与
handleProfileImageUploadToS3()链接起来。然后(()=>{…})

全功能机构:

export const handleProfileImageUploadToS3 = () => {
  return new Promise((resolve, reject) => {
    return (dispatch, getState) => {
      const settingsState = getState().BusinessSettings
      const userState = getState().User
      const imageUpload = true

      if (!settingsState.get('logoImage') || settingsState.get('logoImage') === null) {
        reject('no image selected')
        return
      }
      Utilities.uploadFileToS3(
        imageUpload,
        settingsState.get('logoImage'),
        'apiurl',
        `profiles/${userState.get('id')}`,
        (error, url) => {
          if (error) {
            dispatch(uploadProfileSettingsImageError(error))
            return
          }
          dispatch(updateProfileImageUrlAfterUpload(url))
          resolve(url)
        }
      )
    }
  })
}

经过进一步研究,我发现在返回
newpromise
之前,必须从初始Promise函数返回另一个函数,才能访问Redux
dispatch
getState
方法,而不是在
newpromise()中作为匿名函数返回
代码块,然后它将退出函数执行。以下是完整的代码:

export const handleProfileImageUploadToS3 = () => (dispatch, getState) => {
  return new Promise((resolve, reject) => {
    const settingsState = getState().BusinessSettings
    const userState = getState().User
    const imageUpload = true

    if (!settingsState.get('logoImage') || settingsState.get('logoImage') === null) {
      reject('no image selected')
      return
    }
    Utilities.uploadFileToS3(
      imageUpload,
      settingsState.get('logoImage'),
      'apiurl',
      `profiles/${userState.get('id')}`,
      (error, url) => {
        if (error) {
          dispatch(uploadProfileSettingsImageError(error))
          return
        }
        dispatch(updateProfileImageUrlAfterUpload(url))
        resolve(url)
      }
    )
  })
}

我没有看到您的整个代码库,但是这个函数中有一些我关心的危险信号

  • 状态的
    BusinessSettings
    User
    属性似乎是带有
    get()
    方法的对象
  • 如果S3上传出现错误,则您
    发送
    返回
    ,但您从未
    解决
    拒绝
    承诺
  • reject('no image selected')
    的拒绝不太可能在任何地方被捕获
  • 似乎一个未上传的图像应该存储在UI状态中,并作为参数传递,而不是存储在Redux中。您可能希望在上传后存储URL。这也消除了拒绝的必要性

您在该功能中解决了两个不同的问题,我建议您将这两个问题分开

首先,您有一个函数
Utilities.uploadFileToS3
,它使用成功/失败回调,您希望将其转换为异步函数(返回
承诺的函数)

我将创建一个助手,它只接受变化的参数,而忽略不变的参数(如
'apirl'


既然你已经解决了这一部分,你就可以用一种更典型的方式来写thunk了。您可以通过
.then()
链接或使函数
异步
并使用
try
/
catch
块返回
承诺。到目前为止,我们不需要将整个过程包装在
新承诺中,因为我们已经在
asyncUploadFileToS3
函数中处理过了

您可以从thunk返回一个结果并将其链接,但我不确定这里什么是最有意义的

export const handleProfileImageUploadToS3 = (image) => 
  async ( dispatch, getState ) => {
    const userId = getState().user.id;
    try {
      const url = await asyncUploadFileToS3(image, userId);
      dispatch(updateProfileImageUrlAfterUpload(url));
      return "this is the result";
    } catch (error) {
      dispatch(uploadProfileSettingsImageError(error));
      return "there was an error";
    }
  };
导出默认函数App(){
const dispatch=usedpatch();
const avatar=useSelector((state)=>state.user.imageUrl);
const onClick=()=>{
const image=new Blob();
发送(handleProfileImageUploadToS3(图像)),然后(
//记录“这是结果”或“出现错误”
(返回)=>console.log(“完成”,返回)
);
};
返回(
上传
{化身?化身URL:{化身}:无化身}
);
}

如果(错误)
,您是否需要
解决
拒绝
?看起来它可能永远挂在那里。如果是我个人,我会分别处理这两个问题。这些问题是:1)您需要围绕
实用程序.uploadFileToS3
函数创建一个
Promise
包装器,因为它不是
异步的;2)您需要创建一个使用该函数的thunk分派。当您在此处拒绝“未选择图像”时,是否在任何地方捕获?
settingsState
是您的Redux状态的一部分,但您正在调用
settingsState.get('logoImage')
,这对我来说非常奇怪,因为您的Redux状态应该是原始的可序列化数据。它不应该有
get()
方法。(与
userState.get('id')
)相同。@LindaPaiste它没有挂在
Utilities.uploadFileToS3()
回调中,通过使用断点,我发现函数正在执行第二行代码,返回
(dispatch,getState)=>{}
,但我仍然不确定原因。uploadFileToS3()本身是一个异步函数,但它使用回调而不是承诺。如果它失败,我应该在它的回调中使用reject吗?“2)您需要创建一个使用该函数的thunk分派”-现在使用它与将其作为自己的操作分派有什么区别?它现在运行得很好。@LindaPaiste关于redux商店调用,我不确定,我们在整个项目中都是这样做的,我最近才被介绍到这个项目中,所以我不确定redux的好做法是什么。这就是减速机的外观:
const getInitialState=()=>{return Map({logoImage:'',brandName:'',website:'',countryCode:'',currency:'',vat:'',})
使用
映射不是一个好主意。只需存储该对象,而不在其周围添加
贴图
。谢谢你的全面回答。有很多东西需要吸收。“似乎一个未上传的图像应该存储在您的UI状态中,并作为参数传递,而不是存储在Redux中。”我确实这样做了。我将图像blob存储在本地状态,并仅在单击“保存”按钮时将其保存到redux存储。我将文件对象保存在redux存储中,我使用它上传到S3,获取url,将存储从文件对象更新为S3生成的url,然后调用另一个操作将该url保存到数据库。
export const handleProfileImageUploadToS3 = (image) => 
  async ( dispatch, getState ) => {
    const userId = getState().user.id;
    try {
      const url = await asyncUploadFileToS3(image, userId);
      dispatch(updateProfileImageUrlAfterUpload(url));
      return "this is the result";
    } catch (error) {
      dispatch(uploadProfileSettingsImageError(error));
      return "there was an error";
    }
  };
export default function App() {
  const dispatch = useDispatch();

  const avatar = useSelector((state) => state.user.imageUrl);

  const onClick = () => {
    const image = new Blob();
    dispatch(handleProfileImageUploadToS3(image)).then(
      // logs "this is the result" or "there was an error"
      (returned) => console.log("finished", returned)
    );
  };

  return (
    <div>
      <button onClick={onClick}>Upload</button>
      {avatar ? <div>Avatar URL: {avatar}</div> : <div>No Avatar</div>}
    </div>
  );
}