import { AsynchronousResponseBody } from '../generated/models/AsynchronousResponseBody' import { AsynchronousJobStatus } from '../generated/models/AsynchronousJobStatus' import { delay } from './delay' import { SynapseClientError } from './SynapseClientError' /** * Waits for an asynchronous job to complete. This function will poll the server (based on the implementation of * getAsyncResult) until the job is no longer in the PROCESSING state. * @param getAsyncResult - A function that returns a promise that resolves to the current status of the job, or the job * result. This function should be implemented to call the server using the service-specific API (i.e. this function * should not call GET /asynchronous/job/{jobId}). * @param options * @throws SynapseClientError if the job fails */ export async function waitForAsyncResult< T extends AsynchronousResponseBody | AsynchronousJobStatus, >( getAsyncResult: () => Promise, options?: { onCurrentStatusRetrieved: (response: T) => void }, ): Promise { let currentDelayMs = 125 // 125ms const maxDelayMs = 1000 // 1s let response = await getAsyncResult() while ('jobState' in response && response.jobState === 'PROCESSING') { await delay(currentDelayMs) response = await getAsyncResult() if (options?.onCurrentStatusRetrieved) { options.onCurrentStatusRetrieved(response) } // Exponential backoff for the next request currentDelayMs = Math.min(currentDelayMs * 2, maxDelayMs) } if ('jobState' in response && response.jobState === 'FAILED') { console.error( 'Asynchronous job failed:', response.errorMessage, '\nDetails:\n', response.errorDetails, ) const errorMessage = response.errorMessage ?? response.errorDetails ?? response.exception ?? 'An unknown error occurred.' // TODO: Get the correct HTTP status code for the error. // GET /asynchronous/job/{jobId} does not provide the error (it always returns 200 to get the status). throw new SynapseClientError( 400, errorMessage, `waitForAsyncResult - ${response.requestBody?.concreteType}`, ) } return response }