mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-20 17:14:11 +00:00
51ff72c15e
Summary: Building: - Builds no longer wait for tsc for either client, server, or test targets. All use esbuild which is very fast. - Build still runs tsc, but only to report errors. This may be turned off with `SKIP_TSC=1` env var. - Grist-core continues to build using tsc. - Esbuild requires ES6 module semantics. Typescript's esModuleInterop is turned on, so that tsc accepts and enforces correct usage. - Client-side code is watched and bundled by webpack as before (using esbuild-loader) Code changes: - Imports must now follow ES6 semantics: `import * as X from ...` produces a module object; to import functions or class instances, use `import X from ...`. - Everything is now built with isolatedModules flag. Some exports were updated for it. Packages: - Upgraded browserify dependency, and related packages (used for the distribution-building step). - Building the distribution now uses esbuild's minification. babel-minify is no longer used. Test Plan: Should have no behavior changes, existing tests should pass, and docker image should build too. Reviewers: georgegevoian Reviewed By: georgegevoian Subscribers: alexmojaki Differential Revision: https://phab.getgrist.com/D3506
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
import log from 'app/server/lib/log';
|
|
|
|
/**
|
|
* WorkCoordinator is a helper to do work serially. It takes a doWork() callback which may either
|
|
* do some work and return a Promise, or report no work to be done by returning null. After work
|
|
* completes, doWork() will be called again; when idle, ping() should be called to retry doWork().
|
|
*/
|
|
export class WorkCoordinator {
|
|
private _doWorkCB: () => Promise<void>|null;
|
|
private _tryNextStepCB: () => void;
|
|
private _isStepRunning: boolean = false;
|
|
private _isStepScheduled: boolean = false;
|
|
|
|
/**
|
|
* The doWork() callback will be called on ping() and whenever previous doWork() promise
|
|
* succeeds. If doWork() had nothing to do, it should return null, and will not be called again
|
|
* until the next ping().
|
|
*
|
|
* Note that doWork() should never fail. If it does, exceptions and rejections will be caught
|
|
* and logged, and WorkCoordinator will not be called again until the next ping().
|
|
*/
|
|
constructor(doWork: () => Promise<void>|null) {
|
|
this._doWorkCB = doWork;
|
|
this._tryNextStepCB = () => this._tryNextStep(); // bound version of _tryNextStep.
|
|
}
|
|
|
|
/**
|
|
* Attempt doWork() again. If doWork() is currently running, it will be attempted again on
|
|
* completion even if the current run fails.
|
|
*/
|
|
public ping(): void {
|
|
if (!this._isStepScheduled) {
|
|
this._isStepScheduled = true;
|
|
this._maybeSchedule();
|
|
}
|
|
}
|
|
|
|
private async _tryNextStep(): Promise<void> {
|
|
this._isStepScheduled = false;
|
|
if (!this._isStepRunning) {
|
|
this._isStepRunning = true;
|
|
try {
|
|
const work = this._doWorkCB();
|
|
if (work) {
|
|
await work;
|
|
// Only schedule the next step if some work was done. If _doWorkCB() did nothing, or
|
|
// failed, _doWorkCB() will only be called when an external ping() triggers it.
|
|
this._isStepScheduled = true;
|
|
}
|
|
} catch (err) {
|
|
// doWork() should NOT fail. If it does, we log the error here, and stop scheduling work
|
|
// as if there is no more work to be done.
|
|
log.error("WorkCoordinator: error in doWork()", err);
|
|
} finally {
|
|
this._isStepRunning = false;
|
|
this._maybeSchedule();
|
|
}
|
|
}
|
|
}
|
|
|
|
private _maybeSchedule() {
|
|
if (this._isStepScheduled && !this._isStepRunning) {
|
|
setImmediate(this._tryNextStepCB);
|
|
}
|
|
}
|
|
}
|