blob: 48dffcb08cd09cfac1f0d1795d25e6d413989e4f [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
export class AsyncScope {
static scopes = new Set<AsyncScope>();
static abortSignal: AbortSignal|undefined;
private asyncStack: Array<{description?: string, frames: string[]}> = [];
get descriptions(): string[] {
return this.asyncStack.map(({description}) => description).filter(d => d) as string[];
}
get stack(): null|string[] {
if (this.asyncStack.length === 0) {
return null;
}
return this.asyncStack[this.asyncStack.length - 1].frames;
}
push(description?: string) {
const stack = new Error().stack;
if (!stack) {
throw new Error('Could not get stack trace');
}
if (this.asyncStack.length === 0) {
AsyncScope.scopes.add(this);
}
const frames = stack.split('\n').filter(
value =>
!(value === 'Error' || value.includes('AsyncScope') || value.includes('runMicrotasks') ||
value.includes('processTicksAndRejections')));
this.asyncStack.push({description, frames});
}
pop() {
this.asyncStack.pop();
if (this.asyncStack.length === 0) {
AsyncScope.scopes.delete(this);
}
}
async exec<T>(callable: () => Promise<T>, description?: string) {
this.push(description);
try {
const result = await callable();
return result;
} finally {
this.pop();
}
}
execSync<T>(callable: () => T, description?: string) {
this.push(description);
try {
const result = callable();
return result;
} finally {
this.pop();
}
}
}