在后台创建一个与Promise一起使用的EventEmitter的正确方法

时间:2018-02-14 14:09:48

标签: javascript node.js class promise eventemitter

我正在创建一个“类”,用于发出errordatadownloadFileinitialize等事件。发出请求后会触发每个事件,并且每个事件都由具有相同名称的方法触发:

class MyClass extends EventEmitter {
  constructor(data) {
    this.data = data

    this.initialize()
      .then(this.downloadFile)
      .then(this.data)
      .catch(this.error)
  }

  initialize() {
    const req = superagent.post('url...')
    superagent.send(data)
    const res = await req // this will actually fire the request
    this.emit('initialize')
    this.url = res.body
    return res
  }

  downloadFile() {
    const req = superagent.put(this.url)
    const res = await req; // this will actually fire the request
    req.on('progress', (progress) => this.emit('downloadFile', progress)
    //
    // save to disk
    //
    return res
  }

  data() {
    // Next in the sequence. And will fire the 'data' event: this.emit('data', data)
  }

  error(err) {
    this.emit('error', err)
  }
}

之后我会调用数据方法。我的疑问是:是否有一种设计模式可以在不使用Promise的情况下按顺序调用事件?目前我正在使用链接,但我觉得这不是最好的方法,也许我错了。

this.initialize()
  .then(this.downloadFile)
  .then(this.data)
  .catch(this.error)

但我觉得这可能是一种更好的方法。

bergi问题的答案:

a)为什么使用类语法?

因为从EventEmitter继承更容易,而且我认为它比使用构造函数更具可读性 功能,例如:

function Transformation(data) {
  this.data = data
}

// Prototype stuffs here

b)如何使用此代码

我正在创建一个与我的API进行交互的客户端。意思是用户可以看到后台发生的事情。 E.g:

const data = {
 data: {},
 format: 'xls',
 saveTo: 'path/to/save/xls/file.xls'
}

const transformation = new Transformation(data)

// Events
transformation.on('initialize', () => {
  // Here the user knows that the transformation already started
})

transformation.on('fileDownloaded', () => {
  // Here the file has been downloaded to disk
})

transformation.on('data', (data) => {
  // Here the user can see details of the transformation -
  //   name,
  //   id,
  //   size,
  //   the original object,
  //   etc
})

transformation.on('error', () => {
  // Here is self explanatory, if something bad happens, this event will be fired
})

c)该怎么办?

用户可以将包含数据的对象转换为Excel。

2 个答案:

答案 0 :(得分:1)

听起来您正在创建的transformation对象仅供调用者用于侦听事件。用户不需要具有要获取的属性的class实例或要调用的方法。所以不要做一个。 KISS。

function transform(data) {
  const out = new EventEmitter();
  async function run() {
    try {
      const url = await initialise();
      const data = await downloadFile(url);
      out.emit('data', data);
    } catch(err) {
      out.emit('error', err);
    }
  }

  async function initialise() {
    const req = superagent.post('url...')
    superagent.send(data)
    const res = await req // this will actually fire the request
    out.emit('initialize')
    return res.body
  }

  async function downloadFile(url) {
    const req = superagent.put(url)
    req.on('progress', (progress) => out.emit('downloadFile', progress)
    const res = await req; // this will actually fire the request
    //
    // save to disk
    //
    return data;
  }

  run();
  return out;
}

可能更简单的是省略(仅限一次?)dataerror事件,只是返回一个承诺,与事件发射器一起用于进度通知:

  return {
    promise: run(), // basically just `initialise().then(downloadFile)`
    events: out
  };

答案 1 :(得分:0)

如果您想要另一种方式按顺序调用事件,并且如果您使用支持ES7的Node.js版本,则可以执行以下操作:

class MyClass extends EventEmitter {
  constructor(data) {
    this.data = data;

    this.launcher();
  }

  async launcher() {
    try {
      await this.initialize();
      await this.downloadFile();
      await this.data();
    }
    catch(err) {
      this.error(err);
    }
  }

  initialize() {
    const req = superagent.post('url...');
    superagent.send(data);
    this.emit('initialize');
    this.url = req.body;
    return req;
  }

  downloadFile() {
    const req = superagent.put(this.url);
    req.on('progress', (progress) => this.emit('downloadFile', progress)
    //
    // save to disk
    //
    return req;
  }

  data() {
    // Next in the sequence. And will fire the 'data' event: this.emit('data', data)
  }

  error(err) {
    this.emit('error', err)
  }
}

说明:在函数内部代替await你的Promise,只需在根级别为它们返回Promises和await