假设我有以下类:
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
中的一个方法中。然而,问题是readFileDecorated
在SyncFileReader
中是同步的,而在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)
相同,但至少它比原始解决方案更好,因为它不再有冗余。