对于需要在大文件有用之前异步加载大文件的节点模块,是否存在通用模式?

Is there a common pattern for node modules that need to async load large files before they are useful?

我不喜欢目前为止看到的任何东西...

模块

import lineReader from 'line-reader'

let bigFile = './huge-file.csv'

export default class DooDad {

    constructor() {
        this.dictionary = new Map()
        // Something like this seems like it'd be nice...
        // await this.load()
    }

    load() {
        return new Promise((resolve, reject) => {
            lineReader.eachLine(bigFile, (line, last) => {
                // this.dictionary = The contents of huge-file.csv
            })
        })
    }

    doStuff(foo) {
        // Uses this.dictionary to do something interesting
        // Problem: Unusable without first calling and waiting for this.load()
    }
}

用法

import DooDad from '../doodad'

let doodad = new DooDad()

// We have to first call and wait for load() before doodad is useable
doodad.load().then(x => {
    doodad.doStuff()
})

看来你要么想要...

1) 同步加载

2) 在 DooDad 上创建一个静态 create 函数,return 是一个解析为 DooDad

新实例的承诺

3) 使构造函数 return 成为 Promise(看起来很奇怪)Asynchronous constructor

4) 加载完成后发出事件

5) 保持现状

6) ????

doodad.load().then() 对我来说非常有意义。您不希望构造函数是异步的,因此将 .load() 放在异步内容所在的位置是有意义的。

我见过的另一种模式是导出一个工厂类型的函数,该函数 return 是一个承诺,当该承诺得到解决时,解决的值就是您完全形成的对象。这样做的好处是在异步操作完成之前无法访问对象,并且在它准备好之前调用代码不会尝试使用它。

import makeDooDad from '../doodad'

makeDooDad().then(doodad => {
   // you only get access to the object here after it's been fully
   // initialized
   doodad.doStuff();
});

并且,makeDooDad() 工厂函数在您的模块内部执行类似这样的操作:

function makeDooDad() {
   let d = new DooDad();
   // fully initialize the doodad object before resolving the promise
   // and before returning the object
   return d.load().then(() => {
       // let the object itself by the resolved value
       return d;
   });
}

至于你的其他选择:

Make the loading synchronous

只有在服务器启动时才可以这样做。在服务器启动时做一些同步 I/O 通常没有实际成本,而且通常会使事情变得简单得多。例如,require() 本身做同步 I/O.

Make a static create function on DooDad that returns a promise that resolves to a new instance of a DooDad

这基本上就是我上面推荐的工厂功能。这通常是一个不错的选择。

Make the constructor return a Promise (seems weird) Asynchronous constructor

没有。真的不想那样做。构造函数应该 return 对象,而不是承诺。使用工厂函数 return 承诺。

Emit an event when its done loading

还有其他代码可以执行此操作,例如创建 writeStream,在实际打开流时在流上发出 open 事件。但是,在 promises 的时代,这不是我最喜欢的为尚未使用大量事件的其他类型的对象做事的方式。

Leave it how it is

原样没问题,但我更喜欢return承诺的工厂功能。