1
  • 我有 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 共享状态时)。

如何避免这种情况?被调用者是否有其他更好的方法可以向调用者发出信号,无论它是否应该对返回的承诺做某事。

4

0 回答 0