filter 中不能使用 async/await,因其必须同步返回值,而 async 函数总返回 Promise,导致模板渲染出 [object Promise];应改用 computed/watch 或组合式 API(如 useAsyncMap)配合缓存管理。

过滤器(filter)里不能直接用 async/await 或 Promise,强行写会返回 Promise 对象而不是真实数据,页面渲染必然出错。
为什么 filter 里不能 await
uni-app 的 v-bind 或模板插值中调用的 filter 函数是同步执行的,它期望立刻返回一个字符串或基本类型值。而 async 函数无论有没有 await,返回的都是 Promise;手动 new Promise 同理。一旦你写成:
filters: {
asyncFormat(val) {
return await this.fetchLabel(val); // ❌ 错误:this.fetchLabel 是 async 函数
}
}
模板里 {{ id | asyncFormat }} 渲染出来的就是 [object Promise],不是你要的标签名。
替代方案:用 computed + watch 手动触发加载
真正可行的做法是把“异步映射”从 filter 搬到 data/computed 层,用响应式变量承接结果。典型场景如:列表项中显示用户姓名,但只存了 userId,需查接口补全。
- 在
data中定义缓存对象:userLabels: {} - 用
computed或watch监听原始 ID 列表变化,批量去重请求(避免重复调用) - 渲染时直接读
userLabels[userId],未加载完可显示"-"或"加载中"
示例片段:
data() {
return {
userIds: [1, 2, 3],
userLabels: {}
}
},
watch: {
userIds: {
handler() {
const ids = [...new Set(this.userIds.filter(id => !this.userLabels[id]))];
if (ids.length) {
this.loadUserNames(ids);
}
},
immediate: true
}
},
methods: {
loadUserNames(ids) {
uni.request({
url: '/api/users/names',
data: { ids },
success: res => {
res.data.forEach(item => {
this.$set(this.userLabels, item.id, item.name);
});
}
});
}
}
更干净的写法:封装成组合式 API(uni-app 3.0+)
如果你项目已升级到支持 setup 的版本,推荐抽离为可复用的 hook,比如 useAsyncMap:
- 接收一个 key → value 映射函数(如
id => api.getUser(id)) - 内部维护
cache: ref({})和loading: ref(new Set()) - 暴露
get(key)方法:命中缓存则直接返回;未命中则触发请求并返回 placeholder
这样在 template 里就能安全写 {{ useAsyncMap.get(userId) }},无需担心同步/异步撕裂。
容易忽略的关键点
异步映射类需求最常被低估的是**缓存生命周期和内存泄漏**:用户翻页、切换 tab 后,旧的 userLabels 对象若没清理,会持续占用内存,且可能造成 stale data(比如用户改名后新页没刷新)。建议在 onUnload 或离开页面前清空相关缓存,或改用 Map + WeakMap 控制引用强度。


















