Files
JavaScriptServices/src/Microsoft.AspNetCore.SpaServices/npm/domain-task/src/main.ts
2018-01-25 00:42:58 +00:00

86 lines
3.1 KiB
TypeScript

import * as domain from 'domain';
import * as domainContext from 'domain-context';
// Not using symbols, because this may need to run in a version of Node.js that doesn't support them
const domainTasksStateKey = '__DOMAIN_TASKS';
const domainTaskBaseUrlStateKey = '__DOMAIN_TASK_INTERNAL_FETCH_BASEURL__DO_NOT_REFERENCE_THIS__';
let noDomainBaseUrl: string;
export function addTask<T>(task: PromiseLike<T>): PromiseLike<T> {
if (task && domain.active) {
const state = domainContext.get(domainTasksStateKey) as DomainTasksState;
if (state) {
state.numRemainingTasks++;
task.then(() => {
// The application may have other listeners chained to this promise *after*
// this listener, which may in turn register further tasks. Since we don't
// want the combined task to complete until all the handlers for child tasks
// have finished, delay the response to give time for more tasks to be added
// synchronously.
setTimeout(() => {
state.numRemainingTasks--;
if (state.numRemainingTasks === 0 && !state.hasIssuedSuccessCallback) {
state.hasIssuedSuccessCallback = true;
setTimeout(() => {
state.completionCallback(/* error */ null);
}, 0);
}
}, 0);
}, (error) => {
state.completionCallback(error);
});
}
}
return task;
}
export function run<T>(codeToRun: () => T, completionCallback: (error: any) => void): T {
let synchronousResult: T;
domainContext.runInNewDomain(() => {
const state: DomainTasksState = {
numRemainingTasks: 0,
hasIssuedSuccessCallback: false,
completionCallback: domain.active.bind(completionCallback)
};
try {
domainContext.set(domainTasksStateKey, state);
synchronousResult = codeToRun();
// If no tasks were registered synchronously, then we're done already
if (state.numRemainingTasks === 0 && !state.hasIssuedSuccessCallback) {
state.hasIssuedSuccessCallback = true;
setTimeout(() => {
state.completionCallback(/* error */ null);
}, 0);
}
} catch(ex) {
state.completionCallback(ex);
}
});
return synchronousResult;
}
export function baseUrl(url?: string): string {
if (url) {
if (domain.active) {
// There's an active domain (e.g., in Node.js), so associate the base URL with it
domainContext.set(domainTaskBaseUrlStateKey, url);
} else {
// There's no active domain (e.g., in browser), so there's just one shared base URL
noDomainBaseUrl = url;
}
}
return domain.active ? domainContext.get(domainTaskBaseUrlStateKey) : noDomainBaseUrl;
}
interface DomainTasksState {
numRemainingTasks: number;
hasIssuedSuccessCallback: boolean;
completionCallback: (error: any) => void;
}