创建在后台使用 Promises 的 EventEmitter 的正确方法
Proper way of creating a EventEmitter that works with Promises in the background
我正在创建一个 "class",它发出 error
、data
、downloadFile
和 initialize
等事件。每个事件在发出请求后触发,每个事件都由具有相同名称的方法触发:
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)
}
}
之后我就有了要调用的数据方法。我的疑问是:是否有设计模式可以在不使用 Promises 的情况下按顺序调用事件?目前我正在使用链接,但我觉得这不是最好的方法,也许我错了。
this.initialize()
.then(this.downloadFile)
.then(this.data)
.catch(this.error)
但我觉得这可能是更好的方法。
bergi 问题的答案:
a) 为什么要使用 class 语法?
因为从EventEmitter继承更容易而且我个人认为它比使用构造函数更具可读性
函数,例如:
function Transformation(data) {
this.data = data
}
// Prototype stuffs here
b) 如何使用此代码
我正在创建一个客户端来与我的 API 进行交互。想法是用户可以看到后台发生的事情。例如:
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。
如果您想要另一种方式按顺序调用事件,并且您使用的是 Node.js 支持 ES7 的版本,您可以执行以下操作:
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 设置,只是 return Promise 和 await
在根级别为它们设置。
听起来您正在创建的 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;
}
省略(仅一次?)data
和 error
事件可能更简单,只是 return 一个承诺,以及用于进度通知的事件发射器:
return {
promise: run(), // basically just `initialise().then(downloadFile)`
events: out
};
我正在创建一个 "class",它发出 error
、data
、downloadFile
和 initialize
等事件。每个事件在发出请求后触发,每个事件都由具有相同名称的方法触发:
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)
}
}
之后我就有了要调用的数据方法。我的疑问是:是否有设计模式可以在不使用 Promises 的情况下按顺序调用事件?目前我正在使用链接,但我觉得这不是最好的方法,也许我错了。
this.initialize()
.then(this.downloadFile)
.then(this.data)
.catch(this.error)
但我觉得这可能是更好的方法。
bergi 问题的答案:
a) 为什么要使用 class 语法?
因为从EventEmitter继承更容易而且我个人认为它比使用构造函数更具可读性 函数,例如:
function Transformation(data) {
this.data = data
}
// Prototype stuffs here
b) 如何使用此代码
我正在创建一个客户端来与我的 API 进行交互。想法是用户可以看到后台发生的事情。例如:
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。
如果您想要另一种方式按顺序调用事件,并且您使用的是 Node.js 支持 ES7 的版本,您可以执行以下操作:
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 设置,只是 return Promise 和 await
在根级别为它们设置。
听起来您正在创建的 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;
}
省略(仅一次?)data
和 error
事件可能更简单,只是 return 一个承诺,以及用于进度通知的事件发射器:
return {
promise: run(), // basically just `initialise().then(downloadFile)`
events: out
};