javascript - 让 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()
解决方案
我想你可以使用自定义钩子。就像是 -
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>
}
推荐阅读
- webpack - Workbox 的预缓存清单文件在 Laravel Mix 设置中包含无效的 URL 字符串
- javascript - 编译空行列表并将它们隐藏在 Google App Script 中
- php - 如何在jquery中获取数组对象的值
- java - 移动数组列表对象的动画
- excel - 如果两个单元格匹配返回第三个
- javascript - 在 Dialogflow v2 中使用事件输入时无法从 queryParam 获取参数
- javascript - 如何在 Blogger 上为 YouTube 视频制作更大的缩略图?
- android - 通过 fetchAPI 获取数据后,FlatList 在 ios 上呈现,但不在 android 上呈现
- c# - 如何使用枚举进行版本控制
- javascript - Pickadate.js 设置 'from' 'to' 限制