结合 Promise 和 EventEmitter
Combine Promise and EventEmitter
我创建了这个 "simple pattern",它适用于组合 Promise
和 EventEmitter
(使用 nodejs)。
但是:我想知道是否有更好的进球方式?
const { EventEmitter } = require('events');
const fs = require('fs');
function doSomething(parameters) {
const emitter = new EventEmitter();
const promise = new Promise((resolve, reject) => {
// DO DIRTY JOB
fs.readdir(parameters.directory, (err, files) => {
if (err) {
reject(err);
return;
}
files.forEach(file => emitter.emit('update-event', file));
resolve(`I'm done: ${parameters.param} world`);
});
});
return { promise, emitter };
}
const work = doSomething({ param: 'hello', directory: './' });
work.emitter.on('update-event', data => console.log(`Update ${data}`));
work.promise.then(console.log).catch(console.error);
我在想:
doSomething(...).on(...).then(...)
但我不知道该怎么做。
不,您不应该在一个对象中组合事件发射器和承诺。像您一样单独退货,没问题。
当然,在您的特定示例中,根本没有理由使用事件发射器。它只是在承诺实现时触发。更简单:
const fs = require('fs');
function doSomething(parameters) {
return new Promise((resolve, reject) => {
// DO DIRTY JOB
fs.readdir(parameters.directory, (err, files) => {
if (err) reject(err);
else resolve(Object.assign(files, parameters));
});
});
}
doSomething({ param: 'hello', directory: './' }).then(files => {
for (const data of files) {
console.log(`Update ${data}`)
}
return `I'm done: ${files.param} world`;
}).then(console.log, console.error);
我建议这样做:
import EventPromised from "event-promised";
function doSomething(parameters) {
return new EventPromised((resolve, reject, emit) => {
fs.readdir(parameters.directory, (err, files) => {
if (err) {
reject(err);
return;
}
files.forEach(file => emit('update-event', file));
resolve(`I'm done: ${parameters.param} world`);
});
});
}
doSomething({ param: 'hello', directory: './' })
.on('update-event', data => console.log(`Update ${data}`))
.then(console.log)
.catch(console.error);
Node.js 为此构建了一个函数:require('events').once
函数!这里是 PR.
它已与 Node [v11.13] 一起发布 (https://nodejs.org/en/blog/release/v11.13.0/)
用法示例(来自文档):
const { once, EventEmitter } = require('events');
async function run() {
const ee = new EventEmitter();
process.nextTick(() => {
ee.emit('myevent', 42);
});
const [value] = await once(ee, 'myevent');
console.log(value); // 42
const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});
try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}
run();
我个人不确定接受的答案与 OP 的问题有何关系,无论如何我认为我确实找到了一种相当简单(但可能不是很好)的方法来完成特定的 doSomething(...).on(...).then(...)
事情OP问。使用 OP 的示例代码,我们可以执行以下操作:
const { EventEmitter } = require('events');
const fs = require('fs');
function doSomething(parameters) {
var resolves;
var rejects;
const emitter = new EventEmitter();
const promise = new Promise((resolve, reject) => {
resolves = resolve;
rejects = reject;
});
promise.on = emitter.on;
promise.emit = emitter.emit;
// DO DIRTY JOB
fs.readdir(parameters.directory, (err, files) => {
if (err) {
rejects(err);
return;
}
files.forEach(file => promise.emit('update-event', file));
resolves(`I'm done: ${parameters.param} world`);
});
return promise;
}
const work = doSomething({ param: 'hello', directory: './' });
work.on('update-event', data => console.log(`Update ${data}`))
.then(console.log)
.catch(console.error);
到目前为止,它适用于我有限的案例,据我所知,事件和承诺都可以毫无问题地链接起来。对于我还没有遇到过的更复杂的用例,可能存在问题,但它确实起到了像 OP 要求的那样链接 doSomething(...).on(...).then(...)
的目的。
我创建了这个 "simple pattern",它适用于组合 Promise
和 EventEmitter
(使用 nodejs)。
但是:我想知道是否有更好的进球方式?
const { EventEmitter } = require('events');
const fs = require('fs');
function doSomething(parameters) {
const emitter = new EventEmitter();
const promise = new Promise((resolve, reject) => {
// DO DIRTY JOB
fs.readdir(parameters.directory, (err, files) => {
if (err) {
reject(err);
return;
}
files.forEach(file => emitter.emit('update-event', file));
resolve(`I'm done: ${parameters.param} world`);
});
});
return { promise, emitter };
}
const work = doSomething({ param: 'hello', directory: './' });
work.emitter.on('update-event', data => console.log(`Update ${data}`));
work.promise.then(console.log).catch(console.error);
我在想:
doSomething(...).on(...).then(...)
但我不知道该怎么做。
不,您不应该在一个对象中组合事件发射器和承诺。像您一样单独退货,没问题。
当然,在您的特定示例中,根本没有理由使用事件发射器。它只是在承诺实现时触发。更简单:
const fs = require('fs');
function doSomething(parameters) {
return new Promise((resolve, reject) => {
// DO DIRTY JOB
fs.readdir(parameters.directory, (err, files) => {
if (err) reject(err);
else resolve(Object.assign(files, parameters));
});
});
}
doSomething({ param: 'hello', directory: './' }).then(files => {
for (const data of files) {
console.log(`Update ${data}`)
}
return `I'm done: ${files.param} world`;
}).then(console.log, console.error);
我建议这样做:
import EventPromised from "event-promised";
function doSomething(parameters) {
return new EventPromised((resolve, reject, emit) => {
fs.readdir(parameters.directory, (err, files) => {
if (err) {
reject(err);
return;
}
files.forEach(file => emit('update-event', file));
resolve(`I'm done: ${parameters.param} world`);
});
});
}
doSomething({ param: 'hello', directory: './' })
.on('update-event', data => console.log(`Update ${data}`))
.then(console.log)
.catch(console.error);
Node.js 为此构建了一个函数:require('events').once
函数!这里是 PR.
它已与 Node [v11.13] 一起发布 (https://nodejs.org/en/blog/release/v11.13.0/)
用法示例(来自文档):
const { once, EventEmitter } = require('events');
async function run() {
const ee = new EventEmitter();
process.nextTick(() => {
ee.emit('myevent', 42);
});
const [value] = await once(ee, 'myevent');
console.log(value); // 42
const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});
try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}
run();
我个人不确定接受的答案与 OP 的问题有何关系,无论如何我认为我确实找到了一种相当简单(但可能不是很好)的方法来完成特定的 doSomething(...).on(...).then(...)
事情OP问。使用 OP 的示例代码,我们可以执行以下操作:
const { EventEmitter } = require('events');
const fs = require('fs');
function doSomething(parameters) {
var resolves;
var rejects;
const emitter = new EventEmitter();
const promise = new Promise((resolve, reject) => {
resolves = resolve;
rejects = reject;
});
promise.on = emitter.on;
promise.emit = emitter.emit;
// DO DIRTY JOB
fs.readdir(parameters.directory, (err, files) => {
if (err) {
rejects(err);
return;
}
files.forEach(file => promise.emit('update-event', file));
resolves(`I'm done: ${parameters.param} world`);
});
return promise;
}
const work = doSomething({ param: 'hello', directory: './' });
work.on('update-event', data => console.log(`Update ${data}`))
.then(console.log)
.catch(console.error);
到目前为止,它适用于我有限的案例,据我所知,事件和承诺都可以毫无问题地链接起来。对于我还没有遇到过的更复杂的用例,可能存在问题,但它确实起到了像 OP 要求的那样链接 doSomething(...).on(...).then(...)
的目的。