package.build.esm.promisebuffer.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of utils Show documentation
Show all versions of utils Show documentation
Utilities for all Sentry JavaScript SDKs
import { SentryError } from './error.js';
import { rejectedSyncPromise, SyncPromise, resolvedSyncPromise } from './syncpromise.js';
/**
* Creates an new PromiseBuffer object with the specified limit
* @param limit max number of promises that can be stored in the buffer
*/
function makePromiseBuffer(limit) {
const buffer = [];
function isReady() {
return limit === undefined || buffer.length < limit;
}
/**
* Remove a promise from the queue.
*
* @param task Can be any PromiseLike
* @returns Removed promise.
*/
function remove(task) {
return buffer.splice(buffer.indexOf(task), 1)[0] || Promise.resolve(undefined);
}
/**
* Add a promise (representing an in-flight action) to the queue, and set it to remove itself on fulfillment.
*
* @param taskProducer A function producing any PromiseLike; In previous versions this used to be `task:
* PromiseLike`, but under that model, Promises were instantly created on the call-site and their executor
* functions therefore ran immediately. Thus, even if the buffer was full, the action still happened. By
* requiring the promise to be wrapped in a function, we can defer promise creation until after the buffer
* limit check.
* @returns The original promise.
*/
function add(taskProducer) {
if (!isReady()) {
return rejectedSyncPromise(new SentryError('Not adding Promise because buffer limit was reached.'));
}
// start the task and add its promise to the queue
const task = taskProducer();
if (buffer.indexOf(task) === -1) {
buffer.push(task);
}
void task
.then(() => remove(task))
// Use `then(null, rejectionHandler)` rather than `catch(rejectionHandler)` so that we can use `PromiseLike`
// rather than `Promise`. `PromiseLike` doesn't have a `.catch` method, making its polyfill smaller. (ES5 didn't
// have promises, so TS has to polyfill when down-compiling.)
.then(null, () =>
remove(task).then(null, () => {
// We have to add another catch here because `remove()` starts a new promise chain.
}),
);
return task;
}
/**
* Wait for all promises in the queue to resolve or for timeout to expire, whichever comes first.
*
* @param timeout The time, in ms, after which to resolve to `false` if the queue is still non-empty. Passing `0` (or
* not passing anything) will make the promise wait as long as it takes for the queue to drain before resolving to
* `true`.
* @returns A promise which will resolve to `true` if the queue is already empty or drains before the timeout, and
* `false` otherwise
*/
function drain(timeout) {
return new SyncPromise((resolve, reject) => {
let counter = buffer.length;
if (!counter) {
return resolve(true);
}
// wait for `timeout` ms and then resolve to `false` (if not cancelled first)
const capturedSetTimeout = setTimeout(() => {
if (timeout && timeout > 0) {
resolve(false);
}
}, timeout);
// if all promises resolve in time, cancel the timer and resolve to `true`
buffer.forEach(item => {
void resolvedSyncPromise(item).then(() => {
if (!--counter) {
clearTimeout(capturedSetTimeout);
resolve(true);
}
}, reject);
});
});
}
return {
$: buffer,
add,
drain,
};
}
export { makePromiseBuffer };
//# sourceMappingURL=promisebuffer.js.map