UNPKG

1.96 kBJavaScriptView Raw
1'use strict'
2
3// like a promise
4// if a value is added, it is queued until the first then()
5// if then() is called before a value is available,
6// the returned promise is deferred
7// and queued up for later resolution eg when the next value is available or the
8// Result is completed.
9
10const CLOSED = Promise.resolve()
11
12module.exports = class Result {
13 constructor () {
14 this._isClosed = false
15 this._pendingPromises = []
16 this._pendingVals = []
17
18 // TODO Nice to have ...array argument -> add()
19 }
20
21 add (val) {
22 if (this.isClosed) {
23 throw new Error('cannot add() to a closed Result')
24 }
25
26 if (this._pendingPromises.length > 0) {
27 this._finishPromises(val)
28 } else {
29 this._pendingVals.push(val)
30 }
31 }
32
33 get isClosed () {
34 return this._isClosed
35 }
36
37 get hasValues () {
38 return this._pendingVals.length > 0
39 }
40
41 /**
42 * Requests the next value, wrapped in a promise.
43 * If no more values are available and the Result
44 * is closed, a Promise resolved to undefined is returned.
45 * Else, the returned promise will resolve or reject
46 * when the next value is added.
47 *
48 * @param {Function} res
49 * @param {Function} rej
50 * @return {Promise}
51 */
52 then (res, rej) {
53 if (this.hasValues) {
54 const val = this._pendingVals.shift()
55 const method = val instanceof Error ? 'reject' : 'resolve'
56 return Promise[method](val).then(res, rej)
57 }
58
59 if (this.isClosed) {
60 // no more values will ever be available
61 return CLOSED.then(res, rej)
62 }
63
64 const p = new Promise((resolve, reject) => {
65 this._pendingPromises.push({ resolve, reject })
66 })
67
68 return p.then(res, rej)
69 }
70
71 close (val) {
72 this.add(val)
73 this._isClosed = true
74 }
75
76 _finishPromises (val) {
77 const method = val instanceof Error ? 'reject' : 'resolve'
78
79 let promise
80 while ((promise = this._pendingPromises.shift())) {
81 promise[method](val)
82 }
83 }
84}