问题描述
说我有以下课程:
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
应该只公开同步语法。
另一种解决方法是使用readFileDecoratedPre(filePath)
之前或之后(分别)调用readFileDecoratedPost(contents)
和readFileInternal
的方法,但这在一种方法中不是可行的解决方案包含多个同步/异步调用。
解决方法
您可以使用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'});
}
}
我承认这有点作弊,因为callback
中的_readFileDecorated
函数与readFileDecoratedPost(contents)
本质上是相同的想法,但是至少它比原始解决方案要好,因为它不再有冗余。