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

如何在Javascript/Typescript中将代码从同步类和异步类概括为抽象类,javascript,typescript,asynchronous,abstract-class,Javascript,Typescript,Asynchronous,Abstract Class,假设我有以下课程: abstract class AbstractFileReader { // ??? } class SyncFileReader extends AbstractFileReader { public readFileDecorated(filePath: string): string { console.log('Filepath: '); console.log(filePath); const contents = this.rea

假设我有以下课程:

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'));
抽象类AbstractFileReader{
// ???
}
类SyncFileReader扩展了AbstractFileReader{
public readfiledecorded(filePath:string):string{
log('Filepath:');
log(文件路径);
const contents=this.readFileInternal(文件路径);
log('文件长度:');
console.log(contents.length);
返回内容;
}
private readFileInternal(文件路径:字符串):字符串{
返回fs.readFileSync(文件路径,{'encoding':'utf8'});
}
}
类AsyncFileReader扩展了AbstractFileReader{
//高度冗余的代码。。。
公共异步readfiledecorded(filePath:string):承诺{
log('Filepath:');
log(文件路径);
const contents=wait this.readFileInternal(filePath);
log('文件长度:');
console.log(contents.length);
返回内容;
}
私有异步readFileInternal(文件路径:字符串):承诺{
return wait fs.promises.readFile(文件路径,{'encoding':'utf8'});
}
}
const syncFileReader=新的syncFileReader();
const asyncFileReader=new asyncFileReader();
asyncFileReader.ReadFileDepartmented('./test.txt')。然后((内容)=>{
控制台日志(内容);
}).catch((原因)=>console.log('abc');
//在AbstractFileReader中进行更改后,以下调用应该仍然可以正常工作。
log(syncFileReader.readFileDecorated('./test.txt');
ReadFileReader
中的代码(当然只是一个愚蠢的例子)是高度冗余的,因此我想将其放在
AbstractFileReader
中的一个方法中。但是,问题是
readfilereader
SyncFileReader
中是同步的,而在
AsyncFileReader
中是异步的

我提出的简单解决方案是使
AbstractFileReader
中的所有内容都异步。这会起作用,但是最后一行中的调用必须更改,我不想这样做,因为
SyncFileReader
应该只公开同步语法


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

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

这样,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'})));
  }
}
类SyncFileReader扩展了AbstractFileReader{
public readfiledecorded(filePath:string):承诺{
log('Filepath:');
log(文件路径);
const contents=this.readFileInternal(文件路径);
log('文件长度:');
console.log(contents.length);
返回新承诺((解析)=>解析(内容));
}
private readFileInternal(文件路径:字符串):承诺{
返回新承诺((resolve)=>resolve(fs.readFileSync(filePath,{'encoding':'utf8'}));
}
}
您还可以检查从方法返回的值是否是承诺,如果是,则等待它

const promiseContents: string|Promise<string> = this.readFileInternal(filePath);
let contents: string;
if (typeof contents?.then === 'function') {
    contents = await promiseContents
} else {
    contents = promiseContents
}
const promiseContents:string | Promise=this.readFileInternal(filePath);
let内容:字符串;
如果(内容类型?.then==‘功能’){
内容=等待承诺内容
}否则{
内容=承诺内容
}

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

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

首先,我创建了一个函数
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;
  }  
}
函数handleValue(sync:boolean,valueOrPromise:T | Promise,callback:(T:T)=>void):T | Promise{
如果(同步){
常量值=值或表示为T;
回调(值);
返回值;
}否则{
const promise=价值或承诺;
承诺。然后(回拨);
回报承诺;
}  
}
然后,我对类做了如下更改(注意,我稍微更改了方法签名):

抽象类AbstractFileReader{
受保护的_readfiledecorded(filePath:string,sync:boolean):string | Promise{
log('Filepath:');
log(文件路径);
const contentsOrPromise=this.\u readFileInternal(filePath);
常量回调=(内容:字符串):void=>{
log('文件长度:');
console.log(contents.length);
};
handleValue(同步、内容或调出、回调);
返回内容或选择;
}
受保护的摘要_readFileInternal(filePath:string):string | Promise;
}
类SyncFileReader扩展了AbstractFileReader{
public readfiledecorded(filePath:string):string{
返回此值。_readfiledecorded(filePath,true)作为字符串;
}
受保护的\u readFileInternal(文件路径:字符串):字符串{
返回fs.readFileSync(文件路径,{'encoding':'utf8'});
}
}
类AsyncFileReader扩展了AbstractFileReader{
公共异步readfiledecorded(filePath:string):承诺{
返回此值。\u readfiledecorded(filePath,false);
}
受保护的异步\u readFileInternal(文件路径:字符串):承诺{
return wait fs.promises.readFile(文件路径,{'encoding':'utf8'});
}
}

我承认这有点欺骗,因为
\u readFileDecorated
中的
回调
函数与
readFileDecoratedPost(contents)
基本上是相同的想法,但至少它比原来的解决方案好,因为它不再有冗余。

谢谢,但最后一次调用
console.log(syncFileReader.readFileDecorated('./test.txt'));
然后将记录承诺对象,而不是字符串。我不想更改
SyncFileReader
的同步语法。如果您知道将获得一个
SyncFileReader
并希望保持同步,则不应尝试概括此代码,可以在超类或实用程序文件中的函数中提取公共代码。将其设置为General铝溶液与强迫
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'});
  }
}