Машинопись: Как передать собственность через это в decorator?

#typescript #typescript-decorator

Вопрос:

У меня есть декоратор повторных попыток, который я помещаю поверх всех своих http-вызовов для зависимости.

Что-то вроде:

 @retry(3) callToGoogle(..)  
 @retry(2) callToMicrosoft(..)  

Мне нужен способ определить, какая попытка повторения выполняется внутри функций. У меня нет возможности добавить попытку в качестве параметра в функцию, потому что эти функции имеют необязательные параметры, и в разных местах моего кода они вызываются с разным количеством параметров.

По какой — то причине, когда я развертываю этот исходный код в рабочей среде, значение retryAttempt в функции foo не определено. Странно, но это работает в модульном тесте — это означает retryAttempt , что он заполнен. Я понятия не имею, что это не работает.

Локальный тест-это node.js процесс, в котором находится мое приложение, и среда prod-это kubernetes, которая запускает этот процесс в нескольких блоках. Я не вижу причины, по которой это имело бы значение. Пожалуйста, помогите мне!

Код:

 function retrySyncMethod(  times: number,  target: any,  originalMethod: any,  args: any[]): any {  for (var attempt = 1; attempt lt; times; attempt  ) {  const nextAttempt: number = attempt   1;  target.currAttempt = nextAttempt;  var result = originalMethod.apply(target, args);  return result;  } }  export function retry(attempts: number):  (target: Object,  propertyKey: string,  descriptor: TypedPropertyDescriptorlt;anygt;) =gt; void {  return function (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptorlt;anygt;): TypedPropertyDescriptorlt;anygt; {  var originalMethod = descriptor.value;    descriptor.value = function (...args: any[]): any {  var that = this;   try {  return originalMethod.apply(that, args);   } catch (err) {  return retrySyncMethod(attempts, that, originalMethod, args);  }  };  return descriptor;  }; }  @retry(3) function foo(host: string, path: string, optionaParam?: null) {  const retryAttempt: number = (lt;anygt;this).currAttempt;  if (retryAttempt gt; 2) {  host = "changing host"  }  console.log("Mock Executing http request and throwing an error to simulate the need of retry");  throw Error("Got 500 - throwing error in order to retry"); }  

Ответ №1:

Как насчет того, чтобы перенести логику изменения хоста за пределы вашего фактического метода и позволить декоратору вызвать его? Как показано ниже:

 function changeHostIfNecessary(args: any[], attempt: Number, host: string): any[] {  if ( attempt gt; 3 ) {  const [, ...rest] = args  return [host, rest]  }  return args }  function retry(attempts: number, fallbackHost: string){  return function (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptorlt;anygt;): TypedPropertyDescriptorlt;anygt; {  var originalMethod = descriptor.value;   descriptor.value = function (...args: any[]): any {  var that = this;  let currentAttempt = 1  do {  try {  const modArgs = changeHostIfNecessary(args, currentAttempt, fallbackHost)  originalMethod.apply(that, modArgs);   } catch (err) {  currentAttempt    }    } while(currentAttempt lt;= attempts)     };  return descriptor;  }; }   class Google{  @retry(5, "alternateHost")  public call(host: string, path: string, optionaParam?: null){  console.log(`Calling with host :${host}...`)  throw Error("Simulating a runtime exception.....")  } }  const g = new Google() g.call('InitialHost','doc/xyz')