- 我有 Ember 代码,其中后端 API 调用被抽象为一个单独的服务。该服务使用 ember-ajax 库进行后端调用。
- 此服务设置公共标头、处理超时错误和 4xx/5xx 错误。像 422(验证错误)这样的其他任何事情都留给调用代码处理。
-
getCustomerProfile (authenticationToken) {
const backendService = this.get('callBackendService');
return backendService.callEndpoint(GET_METHOD,
getCustomerProfileAPI.url,
{'X-Auth-Token': authenticationToken}).then((customerProfileData) => {
if (!backendService.get('didAnybodyWin') && customerProfileData) {
backendService.set('didAnybodyWin', true);
return customerProfileData.profiles[0];
}
}).catch((error) => {
if (isInvalidError(error)) {
if (!backendService.get('didAnybodyWin')) {
backendService.set('didAnybodyWin', true);
backendService.transitionToErrorPage();
return;
}
}
});
}
并且call-backend-service看起来像这样
callEndpoint (httpVerb, endPoint, headersParameter, data = {},
timeoutInMillisecs = backendCallTimeoutInMilliseconds) {
const headersConst = {
'Content-Type': 'application/vnd.api+json',
'Accept': 'application/vnd.api+json',
'Brand': 'abc'
};
var headers = Ember.assign(headersParameter, headersConst);
var promiseFunctionWrapper;
this.set('didAnybodyWin', false);
if (httpVerb.toUpperCase() === GET_METHOD) {
Ember.Logger.warn('hit GET Method');
promiseFunctionWrapper = () => {
return this.get('ajax').request(endPoint, {headers});
};
} else if (httpVerb.toUpperCase() === POST_METHOD) {
Ember.Logger.warn('hit POST Method');
promiseFunctionWrapper = () => {
return this.get('ajax').post(endPoint, {data: data, headers: headers});
};
}
return RSVP.Promise.race([promiseFunctionWrapper(), this.delay(timeoutInMillisecs).then(() => {
if (!this.get('didAnybodyWin')) {
this.set('didAnybodyWin', true);
Ember.Logger.error('timeout of %s happened when calling the endpoint %s', backendCallTimeoutInMilliseconds, endPoint);
this.transitionToErrorPage();
return;
}
})]).catch((error) => {
if (!this.get('didAnybodyWin')) {
if (isTimeoutError(error)) {
this.set('didAnybodyWin', true);
Ember.Logger.warn('callBackEndService: isTimeoutError(error) condition is true');
this.transitionToErrorPage();
return;
} else if (isAjaxError(error) && !isInvalidError(error)) { //handles all errors except http 422 (inValid request)
this.set('didAnybodyWin', true);
Ember.Logger.warn('callBackEndService: isAjaxError(error) && !isInvalidError(error) [[ non timeout error]] condition is true');
this.transitionToErrorPage();
return;
} else {
throw error; // to be caught by the caller
}
}
});
},
callEndpoint 执行 RSVP.Promise.race 调用以确保调用的后端 API 在超时发生之前返回。它运行两个承诺,首先解决的就是获胜的。didAnybodyWin是保护两个 Promise 不被执行的标志。
到这部分为止一切都很好。
但是这个 didAnybodyWin 成为这个 call-backend-service 的共享状态,因为它必须向调用者传达它是否运行了默认的 then 或 catch 块集,或者它是否希望调用者运行它的 then/catch 块。
问题是当 model() 钩子运行时,我正在做
RSVP.all {
backendAPICall1(),
backendAPICall2(),
backendAPICAll3()
}
此 RSVP.all 将一个接一个地执行所有 3 个调用,因此它们将以交错的方式访问 call-backend-service 并因此冒着相互踩踏的风险(当涉及到 didAnybodyWin 共享状态时)。
如何避免这种情况?被调用者是否有其他更好的方法可以向调用者发出信号,无论它是否应该对返回的承诺做某事。