我们如何正确使用AsyncIterator

问题描述 投票:0回答:1

我正在尝试定义一个 AsycIterator 并使用它。定义作品但使用它一直是一个问题。这是我的代码,请注意与问题无关的其他代码已被删除

这就是我定义迭代器的方式

export interface IDriver {

  tables(): AsyncIterator<Table>
  
 // code removed
}

这就是我的实现方式。

export class MySql implements IDriver {
  async *tables(): AsyncIterator<Table> {
    const sql = `select table_name, table_type, table_rows 
                 from information_schema.tables 
                 where table_schema = ${this.schema} 
                 order by table_name;`

    const rows = await this.query<{
      table_name: string
      table_type: string
      table_rows: number
    }>(sql)

    for (const row of rows) {
      yield {
        name: row.table_name,
        rows: row.table_rows,
        type: row.table_type
      } as Table
    }
  }

 // ---
}

这是我的 package.json 文件

{
  "compilerOptions": {
    "target": "ESNext" ,
    "lib": [
      "ES2018.AsyncIterable",
      "DOM",
      "ESNext"
    ] ,
    "jsx": "preserve" ,
    "experimentalDecorators": true ,
    "emitDecoratorMetadata": true ,
    "useDefineForClassFields": true ,
    "moduleDetection": "auto" ,
    
    "module": "NodeNext", 
    "rootDir": "src/" ,
    "moduleResolution": "NodeNext" ,
  
    "allowUmdGlobalAccess": true ,
    "resolvePackageJsonExports": true ,
    "resolvePackageJsonImports": true ,
    "resolveJsonModule": true ,
    "checkJs": true ,
     
    "declaration": true ,
    "declarationMap": true ,
    "sourceMap": true ,
     "outDir": "./build" ,
    "removeComments": true ,
   
    "newLine": "crlf" ,
    "stripInternal": true ,
    "preserveConstEnums": true ,
      
    "allowSyntheticDefaultImports": true ,
    "esModuleInterop": true ,
    "forceConsistentCasingInFileNames": true ,

    "strict": true ,
    "noImplicitAny": true ,
    "strictNullChecks": true ,
    "strictFunctionTypes": true ,
    "strictBindCallApply": true ,
    "strictPropertyInitialization": true ,
    "noImplicitThis": true ,
    "useUnknownInCatchVariables": true ,
    "alwaysStrict": true ,
    "noUnusedLocals": true ,
    "noUnusedParameters": true ,
    "exactOptionalPropertyTypes": true ,
    "noImplicitReturns": true ,
    "noFallthroughCasesInSwitch": true ,
    "noUncheckedIndexedAccess": true ,
    "noImplicitOverride": true ,
    "noPropertyAccessFromIndexSignature": true ,
    "skipLibCheck": true ,
  },
  "include": ["./src/*"]
}

这就是上面函数的用法

for await (const it of mysql.tables()) {
    print(it)
  }

这给了我一个错误

src/main.ts:13:26 - 错误 TS2504:类型“AsyncIterator”必须具有返回的 [Symbol.asyncIterator] () 方法 异步迭代器。

如果我尝试探索它,这就是我在类型定义中找到的内容:

interface AsyncIterable<T> {
    [Symbol.asyncIterator](): AsyncIterator<T>;
}
interface SymbolConstructor {
    /**
     * A method that returns the default async iterator for an object. Called by the semantics of
     * the for-await-of statement.
     */
    readonly asyncIterator: unique symbol;
}
declare var Symbol: SymbolConstructor;

我也尝试过这里给出的解决方案:

异步迭代器如何工作?错误 TS2504:类型必须具有返回异步迭代器的“[Symbol.asyncIterator]()”方法

这是一个 6 年前的问题,对我来说根本不起作用。 core-js/shim 没有效果。我不知道该解决方案是否仅对特定版本的打字稿有效。我使用的是5.5.4版本。下面给出了我能够使用 AsyncIterator 的唯一方法。

  const rows = await mysql.tables()
  let item = await rows.next()
  while (!item.done) {

    item = await rows.next()
  }

这显然是一种奇怪的使用 AsyncIterator 的方式,真的,如果这是解决方案,那么也许不使用 AsyncIterator 是最好的选择

有谁知道需要进行哪些更改才能使await()的代码正常工作或者我需要更改我的代码吗?

请注意,我无法返回数据集的数组,因为确实需要逐行处理。

typescript async-await iterator
1个回答
0
投票

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of

forawait...of 语句创建一个迭代 async 的循环 可迭代对象以及同步可迭代对象。这个说法只能是 在可以使用await的上下文中使用,其中包括内部 异步函数体和模块中。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols

为了可迭代,对象必须实现 Symbol.iterator 方法,意味着该对象(或其中之一) 其原型链上的对象)必须具有一个属性 [Symbol.iterator] 键可通过常量 Symbol.iterator 获得:

Symbol.iterator 一个返回对象的零参数函数, 符合迭代器协议。

每当一个对象需要迭代时(例如在一个对象的开头) for...of 循环),它的 Symbol.iterator 方法被调用时没有 参数,返回的迭代器用于获取值 被迭代。

https://tc39.es/ecma262/#sec-asynciterable-interface

%Symbol.asyncIterator% - 返回 AsyncIterator 的函数 object - 返回的对象必须符合 AsyncIterator 接口。

为了简短起见,像这样迭代

mysql.tables()
...

for await (const it of mysql.tables()) {/*...*/}

.tables()
必须返回一个可迭代对象,因此为了使您的代码正常工作,您需要使用
AsyncIterable
而不是
AsyncIterator
:

interface IDriver {
  tables(): AsyncIterable<Table>;
}

/*...*/

async *tables(): AsyncIterable<Table> {/*...*/}

TS 游乐场演示

interface Table {
  name: string;
  rows: number;
  type: string;
}

interface IDriver {
  tables(): AsyncIterable<Table>;
}

class MySql implements IDriver {
  
  private schema: string = 'myschema';    
  private async query<T>(sql: string): Promise<T[]> {        
    return [
      { table_name: 'table1', table_type: '***', table_rows: 10 },
      { table_name: 'table2', table_type: '***', table_rows: 20 },
    ] as unknown as T[];
  }  
 
  async *tables(): AsyncIterable<Table> {
    const sql = `select table_name, table_type, table_rows 
                 from information_schema.tables 
                 where table_schema = '${this.schema}' 
                 order by table_name;`;

    const rows = await this.query<{
      table_name: string;
      table_type: string;
      table_rows: number;
    }>(sql);
    
    for (const row of rows) {
      yield {
        name: row.table_name,
        rows: row.table_rows,
        type: row.table_type,
      } as Table;
    }
  }
}

async function run() {
  const mysql = new MySql();
  
  for await (const table of mysql.tables()) {
    console.log(table);
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.