首页 > 解决方案 > 让 javascript 函数将对自身的引用传递给另一个函数

问题描述

我发现自己不断地为异步调用编写相同形式的代码,因此我尝试将其包装在可以抽象一些细节的东西中。我希望的是,在我的 onError 回调中,我可以传递正在执行的异步函数的引用,以便某些中间件可以在必要时实现重试逻辑。也许这是一种代码味道,我以错误的方式解决了这个问题,但我很好奇它是否可能,或者是否有其他处理这个问题的建议。

const runAsync = (asyncFunc) => {
  let _onBegin = null;
  let _onCompleted = null;
  let _onError = null;
  let self = this;
  return {
    onBegin(f) {
      _onBegin = f;
      return this;
    },
    onCompleted(f) {
      _onCompleted = f;
      return this;
    },
    onError(f) {
      _onError = f;
      return this;
    },
    async execute() {
      if (_onBegin) {
        _onBegin();
      }
      try {
        let data = await asyncFunc();
        if (_onCompleted) {
          _onCompleted(data);
        }
      } catch (e) {
        if (_onError) {
          _onError(e ** /*i'd like to pass a function reference here as well*/ ** );
        }
        return Promise.resolve();
      }
    },
  };
};

await runAsync(someAsyncCall())
  .onBegin((d) => dispatch(something(d)))
  .onCompleted((d) => dispatch(something(d)))
  .onError((d, func) => dispatch(something(d, func)))
  .execute()

标签: javascriptreduxcallbackreact-reduxmodule-pattern

解决方案


我想你可以使用自定义钩子。就像是 -

import { useState, useEffect } from 'react'

const useAsync = (f) => {
  const [state, setState] =
    useState({ loading: true, result: null, error: null })

  const runAsync = async () => {
    try {
      setState({ ...state, loading: false, result: await f })
    }
    catch (err) {
      setState({ ...state, loading: false, error: err })
    }
  }

  useEffect(_ => { runAsync() }, [])

  return state
}

现在我们可以在组件中使用它——

const FriendList = ({ userId }) => {
  const response =
    useAsync(UserApi.fetchFriends(userId)) // <-- some promise-returning call

  if (response.loading)
    return <Loading />
  else if (response.error)
    return <Error ... />
  else
    return <ul>{response.result.map(Friend)}</ul>
}

自定义钩子 api 非常灵活。上面的做法很幼稚,但我们可以多想一想,让它更实用——

import { useState, useEffect } from 'react'

const identity = x => x

const useAsync = (runAsync = identity, deps = []) => {
  const [loading, setLoading] = useState(true)
  const [result, setResult] = useState(null)
  const [error, setError] = useState(null)

  useEffect(_ => { 
    Promise.resolve(runAsync(...deps))
      .then(setResult, setError)
      .finally(_ => setLoading(false))
  }, deps)

  return { loading, error, result }
}

自定义挂钩是涂料。我们可以使用其他自定义钩子制作自定义钩子 -

const fetchJson = (url = "") =>
  fetch(url).then(r => r.json())  // <-- stop repeating yourself

const useJson = (url = "") => // <-- another hook
  useAsync(fetchJson, [url]) // <-- useAsync

const FriendList = ({ userId }) => {
  const { loading, error, result } =
    useJson("some.server/friends.json") // <-- dead simple

  if (loading)
    return <Loading .../>

  if (error)
    return <Error .../>

  return <ul>{result.map(Friend)}</ul>
}

推荐阅读