Ember.js计算属性未等待异步RSVP Promise致值未定义求助
Ember.js计算属性处理异步Promise导致值为undefined的问题解决
问题根源
getCount计算属性无返回值:你内部调用RSVP.all()后没有返回任何结果,导致计算属性默认返回undefined。即使补上return RSVP.all(...),普通计算属性也只会返回Promise对象,不会自动等待其解析出实际数值。- 计算属性是同步执行的:Ember标准计算属性不支持异步逻辑,不会等待Promise完成就直接取值,因此
isVisible拿到的要么是undefined,要么是未解析的Promise对象,而非期望的count数值。
解决方案
方案一:使用PromiseProxyMixin(适配经典类写法)
通过PromiseProxyMixin把Promise包装成代理对象,Promise解析后代理的content属性会自动更新,计算属性可依赖该属性响应变化:
import Component from '@ember/component'; import { computed } from '@ember/object'; import { inject as service } from '@ember/service'; import RSVP from 'rsvp'; import PromiseProxyMixin from '@ember/object/promise-proxy-mixin'; const PromiseProxy = RSVP.Promise.extend(PromiseProxyMixin); export default Component.extend({ classNames: ['count'], countService: service('count'), countProxy: computed(function () { return PromiseProxy.create({ promise: this.get('countService').getCount().then(count => Number(count)) }); }), isVisible: computed('countProxy.content', function () { let count = this.get('countProxy.content'); // 兼容Promise未完成的状态 return typeof count === 'number' && count > 0; }), });
方案二:生命周期钩子+普通属性(直观异步处理)
在组件插入DOM后调用异步服务,拿到结果后更新普通属性,计算属性依赖该属性:
import Component from '@ember/component'; import { computed } from '@ember/object'; import { inject as service } from '@ember/service'; export default Component.extend({ classNames: ['count'], countService: service('count'), count: undefined, // 初始化属性 didInsertElement() { this._super(...arguments); this.get('countService').getCount() .then(count => this.set('count', Number(count))); }, isVisible: computed('count', function () { let count = this.get('count'); return typeof count === 'number' && count > 0; }), });
如果需要组件重新渲染时重新获取数据,可将逻辑移至didRender或监听特定属性变化触发请求。
方案三:Ember Octane+ 推荐写法(Glimmer组件)
如果项目已升级到Octane,使用@tracked和ember-concurrency处理异步更简洁:
import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; import { tracked } from '@glimmer/tracking'; import { task } from 'ember-concurrency'; export default class CountComponent extends Component { @service countService; @tracked count; @task *fetchCount() { let count = yield this.countService.getCount(); this.count = Number(count); } get isVisible() { return typeof this.count === 'number' && this.count > 0; } constructor() { super(...arguments); this.fetchCount.perform(); } }
内容的提问来源于stack exchange,提问作者HurkanSeyhan
相关产品推荐
相关产品推荐

