Aller au contenu principal
Version: 3.0.0

Retryable

Starting from Midway v3.5.0, method custom retry logic is supported.

Many times, we need to use try multiple times to wrap the function and handle errors on some method calls that are prone to failure or asynchronous.

For example:

// Defines an asynchronous function

async function invoke(id) {

// Some remote call logic

}


async invokeNew() {
let error;
try {
return await invoke(1);
} catch(err) {
error = err;
}

try {
return await invoke(2);
} catch(err) {
error = err;
}

if (error) {
// ....
}
}

You may try to call the invoke operation multiple times and use the try/catch to catch exceptions, resulting in repeated and lengthy business code writing.

Define retry functions

We can use retryWithAsync method to package and simplify the whole process.

import { retryWithAsync } from '@midwayjs/core';

async function invoke(id) {
// ...
}

async function someServiceMethod() {
// The default call, plus two retries, can be executed up to three times.
const invokeNew = retryWithAsync(invoke, 2);

try {
return await invokeNew(1);
} catch(err) {

// err
}
}

The method parameters and return values after the package are exactly the same as the original method.

When the call is successful within the retry period and no error is thrown, the successful return value will be returned.

If it fails, the MidwayRetryExceededMaxTimesError exception will be thrown.

If it is used in a class, you may need to pay attention to the point of this.

Examples are as follows:

import { retryWithAsync } from '@midwayjs/core';

export class UserService {

async getUserData(userId: string) {
// wrap
const getUserDataOrigin = retryWithAsync(
this.getUserDataFromRemote,
2,
{
receiver: this
}
);

// invoke
return getUserDataOrigin(userId);
}

async getUserDataFromRemote(userId: string) {
// get data from remote
}
}

This binding

Starting from Midway v3.5.1, a receiver parameter has been added to bind this in the scene of the class for processing:

  • 1, the method of correct this point
  • 2, the correctness of the definition of the package method.
// wrap
const getUserDataOrigin = retryWithAsync(
this.getUserDataFromRemote,
2,
{
receiver: this, // This parameter is used to handle this pointing
}
);

If there is no such parameter, the code needs to be written as follows to bind this, and the definition of the getUserDataOrigin method returned is correct.

// wrap
const getUserDataOrigin = retryWithAsync(
this.getUserDataFromRemote.bind(this) as typeof this.getUserDataFromRemote,
2,
{
receiver: this
}
);


Number of retries

The retryWithAsync provides a second parameter to declare the additional number of retries, which defaults to 1 (only retry once).

This value refers to the number of additional retries after the default call.

Retry of synchronization

Similar to retryWithAsync, we also provide retryWith synchronization method, the parameters and retryWithAsync are almost the same, no additional description.

Retry delay

To prevent frequent retries from putting pressure on the server, you can set the retry interval.

For example:

const invokeNew = retryWithAsync(invoke, 2, {
retryInterval: 2000, //After the execution fails, continue to try again after 2s.
});
astuce

The synchronization method retryWith does not have this attribute.

Error thrown

By default, if the number of retries is exceeded, the MidwayRetryExceededMaxTimesError exception is thrown.

The MidwayRetryExceededMaxTimesError is the default exception of the framework, which can be captured and combed by the error filter, or the original exception can be handled from its attributes.

import { retryWithAsync, MidwayRetryExceededMaxTimesError } from '@midwayjs/core';

async function invoke(id) {
// ...
}

async function someServiceMethod() {
// The default call, plus two retries, can be executed up to three times.
const invokeNew = retryWithAsync(invoke, 2);

try {
return await invokeNew(1);
} catch(err) {
// err.name === 'MidwayRetryExceededMaxTimesError'
// err.cause instanceof CustomError => true
}

}

async invokeNew() {
throw new CustomError('customError');
}

If you want to throw the original error object directly, you can configure the parameters.

For example:

const invokeNew = retryWithAsync(invoke, 2, {
throwOriginError: true
});