blob: 24d044ae4221747a81e198a4a407103e327b7e7e [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001'use strict';
2const pTimeout = require('p-timeout');
3
4module.exports = (emitter, event, opts) => {
5 let cancel;
6
7 const ret = new Promise((resolve, reject) => {
8 if (typeof opts === 'function') {
9 opts = {filter: opts};
10 }
11
12 opts = Object.assign({
13 rejectionEvents: ['error'],
14 multiArgs: false
15 }, opts);
16
17 let addListener = emitter.on || emitter.addListener || emitter.addEventListener;
18 let removeListener = emitter.off || emitter.removeListener || emitter.removeEventListener;
19
20 if (!addListener || !removeListener) {
21 throw new TypeError('Emitter is not compatible');
22 }
23
24 addListener = addListener.bind(emitter);
25 removeListener = removeListener.bind(emitter);
26
27 const resolveHandler = function (value) {
28 if (opts.multiArgs) {
29 value = [].slice.apply(arguments);
30 }
31
32 if (opts.filter && !opts.filter(value)) {
33 return;
34 }
35
36 cancel();
37 resolve(value);
38 };
39
40 const rejectHandler = function (reason) {
41 cancel();
42
43 if (opts.multiArgs) {
44 reject([].slice.apply(arguments));
45 } else {
46 reject(reason);
47 }
48 };
49
50 cancel = () => {
51 removeListener(event, resolveHandler);
52
53 for (const rejectionEvent of opts.rejectionEvents) {
54 removeListener(rejectionEvent, rejectHandler);
55 }
56 };
57
58 addListener(event, resolveHandler);
59
60 for (const rejectionEvent of opts.rejectionEvents) {
61 addListener(rejectionEvent, rejectHandler);
62 }
63 });
64
65 ret.cancel = cancel;
66
67 if (typeof opts.timeout === 'number') {
68 return pTimeout(ret, opts.timeout);
69 }
70
71 return ret;
72};