如何在Javascript/Typescript中将代码从同步和异步类概括为抽象类



假设我有以下类:

abstract class AbstractFileReader {
// ???
}
class SyncFileReader extends AbstractFileReader {
public readFileDecorated(filePath: string): string {
console.log('Filepath: ');
console.log(filePath);
const contents = this.readFileInternal(filePath);
console.log('Length of file: ');
console.log(contents.length);
return contents;
}
private readFileInternal(filePath: string): string {
return fs.readFileSync(filePath, {'encoding': 'utf8'});
}
}
class AsyncFileReader extends AbstractFileReader {
// highly redundant code...
public async readFileDecorated(filePath: string): Promise<string> {
console.log('Filepath: ');
console.log(filePath);
const contents = await this.readFileInternal(filePath);
console.log('Length of file: ');
console.log(contents.length);
return contents;    
}
private async readFileInternal(filePath: string): Promise<string> {
return await fs.promises.readFile(filePath, {'encoding': 'utf8'});
}
}
const syncFileReader = new SyncFileReader();
const asyncFileReader = new AsyncFileReader();
asyncFileReader.readFileDecorated('./test.txt').then((contents) => {
console.log(contents);
}).catch((reason) => console.log('abc'));
// The following call should still work without change after the changes in AbstractFileReader.
console.log(syncFileReader.readFileDecorated('./test.txt'));

readFileDecorated中的代码(当然只是一个愚蠢的例子(是高度冗余的,所以我想把它放在AbstractFileReader中的一个方法中。然而,问题是readFileDecoratedSyncFileReader中是同步的,而在AsyncFileReader中是异步的。

我提出的直接解决方案是在AbstractFileReader中使所有内容异步。这是可行的,但最后一行中的调用必须更改,我不想这样做,因为SyncFileReader应该只公开sync语法。

另一个解决方案是使用在调用readFileInternal之前或之后调用的readFileDecoratedPre(filePath)readFileDecoratedPost(contents)方法,但当一个方法包含多个同步/异步调用时,这不是一个可行的解决方案。

您可以使用Promises使同步代码异步。你可以创建一个承诺并立即解决它。

这样,SyncFileReader中的签名与AsyncFileReader中相同。

class SyncFileReader extends AbstractFileReader {
public readFileDecorated(filePath: string): Promise<string> {
console.log('Filepath: ');
console.log(filePath);
const contents = this.readFileInternal(filePath);
console.log('Length of file: ');
console.log(contents.length);
return new Promise((resolve) => resolve(contents));
}
private readFileInternal(filePath: string): Promise<string> {
return new Promise((resolve) => resolve(fs.readFileSync(filePath, {'encoding': 'utf8'})));
}
}

您还可以检查从方法返回的值是否是Promise,如果是,则等待它

const promiseContents: string|Promise<string> = this.readFileInternal(filePath);
let contents: string;
if (typeof contents?.then === 'function') {
contents = await promiseContents
} else {
contents = promiseContents
}

但这并不是最好的解决方案。

我现在选择了以下解决方案:

首先,我创建了一个函数handleValue,如下所示:

function handleValue<T>(sync: boolean, valueOrPromise: T | Promise<T>, callback: (t: T) => void): T | Promise<T> {
if (sync) {
const value = valueOrPromise as T;
callback(value);
return value;
} else {
const promise = valueOrPromise as Promise<T>;
promise.then(callback);
return promise;
}  
}

然后,我更改了类如下(注意,我稍微更改了方法签名(:

abstract class AbstractFileReader {
protected _readFileDecorated(filePath: string, sync: boolean): string | Promise<string> {
console.log('Filepath: ');
console.log(filePath);
const contentsOrPromise = this._readFileInternal(filePath);
const callback = (contents: string): void => {
console.log('Length of file: ');
console.log(contents.length);
};
handleValue(sync, contentsOrPromise, callback);
return contentsOrPromise;
}
protected abstract _readFileInternal(filePath: string): string | Promise<string>;
}
class SyncFileReader extends AbstractFileReader {
public readFileDecorated(filePath: string): string {
return this._readFileDecorated(filePath, true) as string;
}
protected _readFileInternal(filePath: string): string {
return fs.readFileSync(filePath, {'encoding': 'utf8'});
}
}
class AsyncFileReader extends AbstractFileReader {
public async readFileDecorated(filePath: string): Promise<string> {
return this._readFileDecorated(filePath, false);
}
protected async _readFileInternal(filePath: string): Promise<string> {
return await fs.promises.readFile(filePath, {'encoding': 'utf8'});
}
}

我承认这有点欺骗,因为_readFileDecorated中的callback函数本质上与readFileDecoratedPost(contents)相同,但至少它比原始解决方案更好,因为它不再有冗余。

最新更新