生成函数或异步生成函数

时间:2017-09-14 05:45:08

标签: javascript node.js asynchronous async-await generator

退出生成功能后如何清理。我遇到的问题是我使用Async Iterators第3阶段的EcmaScript提议从文件中读取了一些实用程序,我希望它在退出for-for循环后关闭它正在读取的文件。目前,由于此功能仅适用于第3阶段,因此要运行此功能,您必须使用babel进行转换。

使用以下代码,您可以看到问题。如果您输入一个输入文件,那么它将读取一行并打印出行阅读器仍处于打开状态。

我希望在返回迭代器时显式关闭LineReader类中的文件。

我知道我可以通过不使用生成函数来执行此操作,而是按照概述here返回迭代器对象,但是有什么方法可以保留生成函数并为它定义返回方法。

的src /线reader.js

function deferred() {
  const def = {}
  def.promise = new Promise((resolve, reject) => {
    def.resolve = resolve
    def.reject = reject
  })
  return def
}

/**
 * PromiseQueue from GTOR adapted for ES2015
 * https://github.com/kriskowal/gtor
 */
class PromiseQueue {
  constructor (values) {
    this.ends = deferred();
    if (values) {
      values.forEach(this.put, this);
    }
  }

  put(value) {
    const next = deferred();
    this.ends.resolve({
      head: value,
      tail: next.promise
    });
    this.ends.resolve = next.resolve;
  }

  get () {
    var result = this.ends.promise.then(node=>node.head);
    this.ends.promise = this.ends.promise.then(node=>node.tail)
    return result;
  };
}

class LineReader {
  constructor (input, output) {
    this.lineReader = require('readline').createInterface({ input, output });
    this.lineQueue = new PromiseQueue();
    this.isClosed = false;
    this.lineReader.on('line', (line) => {
      this.lineQueue.put(line);
      this.lineReader.pause();
    });
    this.lineReader.on('close', (line) => {
      this.isClosed = true;
      this.lineQueue.put(Promise.resolve({done: true}));
    });
    this.lineReader.on('SIGCONT', () => {
      // `prompt` will automatically resume the stream
      this.lineReader.prompt();
    });
  }

  readLine(){
    var result = this.lineQueue.get().then(function (data) {
      if(data && data.done) {
        throw new Error("EOF");
      }
      return data
    });
    this.lineReader.resume();
    return result;
  }

  close () {
    this.lineReader.close();
  }

  async * [Symbol.asyncIterator] () {
    try {
      while(!this.isClosed) {
        yield await this.readLine();
      }
    } catch (e) {
      this.close();
    }
  }
}

module.exports = LineReader;

测试/ test.js

var LineReader = require("../src/line-reader");

var lineReader = new LineReader(process.stdin);

(async ()=> {
  var i = 1;
  for await (var line of lineReader) {
    console.log(`${i++} ${line}`);
    break;
  }
  console.log(`The line-reader is ${lineReader.isClosed ? "closed" : "open" }.`);
  lineReader.close();
})().catch(e=> {
  console.error(e)
})

2 个答案:

答案 0 :(得分:3)

您可以始终Object.assign通过被调用的生成函数,并使用return方法扩展其返回的迭代器。

像这样:

class LineReader {
  // ... elided code ...

  async * iterate() {
    try {
      while(!this.isClosed) {
        yield await this.readLine();
      }
    } catch (e) {
      this.close();
    }
  }

  [Symbol.asyncIterator] () {
    return Object.assign(this.iterate(), {
      return: () => {
        this.close();
      }
    });
  }
}

或者你也可以选择这样做,但我更喜欢第一种,因为每次调用它都不会创建一个新函数,而第一次看起来好得多。

class LineReader {
  // ... elided code ...

  [Symbol.asyncIterator] () {
    var that = this;
    return Object.assign(async function * iterate() {
      try {
        while(!that.isClosed) {
          yield await that.readLine();
        }
      } catch (e) {
        that.close();
      }
    }(), {
      return: () => {
        this.close();
      }
    });
  }
}

答案 1 :(得分:1)

只需在finally块中添加try即可。 finally即使函数已经返回也会执行(当有人突然出现for循环时它会返回)。这可以保证您的功能将被清理,您不必修改您的功能。我刚刚发现这一点归功于Jake Archibald的this article

class LineReader {
  // ... elided code ...

  async * [Symbol.asyncIterator] () {
    try {
      while(!this.isClosed) {
        yield await this.readLine();
      }
    } finally {
      this.close();
    }
  }
}