如何结合回调函数使用ember-concurrency实现批量地址验证?
你的核心问题在于第三方API使用的是回调式异步模式,而ember-concurrency的task需要依赖Promise来感知异步操作的完成状态。目前你的BatchQueryAddresses方法没有返回任何值(默认返回undefined),所以yield之后自然拿到的是null/undefined。
解决思路很清晰:把回调风格的API包装成Promise,让task可以通过yield等待这个Promise完成,直到回调触发再继续执行。
第一步:修改ApiService,将回调API包装为Promise
重写BatchQueryAddresses方法,让它返回一个Promise,在Promise内部处理第三方API的回调逻辑:
import Service from '@ember/service'; export default class ApiService extends Service { _api; constructor() { super(...arguments); this._api = API; } BatchQueryAddresses(addressList) { // 返回一个新的Promise return new Promise((resolve, reject) => { // 定义回调函数,在第三方API完成后触发 const handleCallback = (result, error) => { if (error) { // 出错时reject Promise,携带错误信息 reject(new Error(`地址验证失败: ${result}`)); } else { // 成功时resolve Promise,返回格式化后的结果 const formattedResult = JSON.stringify(result, null, ' '); resolve(formattedResult); } }; // 调用第三方API,传入我们的回调函数 this._api.BatchQueryAddress(addressList, 2, handleCallback, null, 2000); }); } }
这样修改后,BatchQueryAddresses就变成了一个返回Promise的方法,ember-concurrency的task可以正确等待它完成。
第二步:更新Component中的task,处理Promise结果
现在你的task可以通过yield等待Promise,并通过try/catch处理成功和错误情况:
import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; import { task } from 'ember-concurrency'; export default class BatchAddressInputComponent extends Component { @service api; addressList = ""; // 从TextArea获取的地址列表 @task *DoAddressValidation () { try { // yield会等待Promise resolve后才继续执行 let results = yield this.api.BatchQueryAddresses(this.addressList); alert(results); // 现在能拿到正确的验证结果了 } catch (error) { // 捕获Promise reject的错误 alert(error.message); } } }
为什么这样可行?
ember-concurrency的task对Promise有原生支持:当你yield一个Promise时,task会暂停执行,直到这个Promise被resolve或reject。如果Promise resolve,task会继续执行并拿到resolve的值;如果Promise reject,task会抛出错误,你可以用try/catch捕获并处理。
额外提示(可选)
如果你的第三方API支持取消正在进行的请求,你还可以利用ember-concurrency的task取消机制,在task被取消时调用API的取消方法,避免不必要的资源消耗。比如:
BatchQueryAddresses(addressList, cancelToken) { return new Promise((resolve, reject) => { const handleCallback = (result, error) => { if (cancelToken.isCancelled) return; // ... 原有逻辑 }; const requestId = this._api.BatchQueryAddress(addressList, 2, handleCallback, null, 2000); // 当cancelToken触发取消时,调用API的取消方法 cancelToken.add(() => { this._api.CancelBatchQuery(requestId); }); }); }
然后在task中使用yield this.api.BatchQueryAddresses(this.addressList, this.DoAddressValidation.cancelToken)即可。
内容的提问来源于stack exchange,提问作者tripper

