• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"use strict";
2module.exports = function(Promise,
3                          PromiseArray,
4                          apiRejection,
5                          tryConvertToPromise,
6                          INTERNAL,
7                          debug) {
8var getDomain = Promise._getDomain;
9var util = require("./util");
10var tryCatch = util.tryCatch;
11
12function ReductionPromiseArray(promises, fn, initialValue, _each) {
13    this.constructor$(promises);
14    var domain = getDomain();
15    this._fn = domain === null ? fn : util.domainBind(domain, fn);
16    if (initialValue !== undefined) {
17        initialValue = Promise.resolve(initialValue);
18        initialValue._attachCancellationCallback(this);
19    }
20    this._initialValue = initialValue;
21    this._currentCancellable = null;
22    if(_each === INTERNAL) {
23        this._eachValues = Array(this._length);
24    } else if (_each === 0) {
25        this._eachValues = null;
26    } else {
27        this._eachValues = undefined;
28    }
29    this._promise._captureStackTrace();
30    this._init$(undefined, -5);
31}
32util.inherits(ReductionPromiseArray, PromiseArray);
33
34ReductionPromiseArray.prototype._gotAccum = function(accum) {
35    if (this._eachValues !== undefined &&
36        this._eachValues !== null &&
37        accum !== INTERNAL) {
38        this._eachValues.push(accum);
39    }
40};
41
42ReductionPromiseArray.prototype._eachComplete = function(value) {
43    if (this._eachValues !== null) {
44        this._eachValues.push(value);
45    }
46    return this._eachValues;
47};
48
49ReductionPromiseArray.prototype._init = function() {};
50
51ReductionPromiseArray.prototype._resolveEmptyArray = function() {
52    this._resolve(this._eachValues !== undefined ? this._eachValues
53                                                 : this._initialValue);
54};
55
56ReductionPromiseArray.prototype.shouldCopyValues = function () {
57    return false;
58};
59
60ReductionPromiseArray.prototype._resolve = function(value) {
61    this._promise._resolveCallback(value);
62    this._values = null;
63};
64
65ReductionPromiseArray.prototype._resultCancelled = function(sender) {
66    if (sender === this._initialValue) return this._cancel();
67    if (this._isResolved()) return;
68    this._resultCancelled$();
69    if (this._currentCancellable instanceof Promise) {
70        this._currentCancellable.cancel();
71    }
72    if (this._initialValue instanceof Promise) {
73        this._initialValue.cancel();
74    }
75};
76
77ReductionPromiseArray.prototype._iterate = function (values) {
78    this._values = values;
79    var value;
80    var i;
81    var length = values.length;
82    if (this._initialValue !== undefined) {
83        value = this._initialValue;
84        i = 0;
85    } else {
86        value = Promise.resolve(values[0]);
87        i = 1;
88    }
89
90    this._currentCancellable = value;
91
92    if (!value.isRejected()) {
93        for (; i < length; ++i) {
94            var ctx = {
95                accum: null,
96                value: values[i],
97                index: i,
98                length: length,
99                array: this
100            };
101            value = value._then(gotAccum, undefined, undefined, ctx, undefined);
102        }
103    }
104
105    if (this._eachValues !== undefined) {
106        value = value
107            ._then(this._eachComplete, undefined, undefined, this, undefined);
108    }
109    value._then(completed, completed, undefined, value, this);
110};
111
112Promise.prototype.reduce = function (fn, initialValue) {
113    return reduce(this, fn, initialValue, null);
114};
115
116Promise.reduce = function (promises, fn, initialValue, _each) {
117    return reduce(promises, fn, initialValue, _each);
118};
119
120function completed(valueOrReason, array) {
121    if (this.isFulfilled()) {
122        array._resolve(valueOrReason);
123    } else {
124        array._reject(valueOrReason);
125    }
126}
127
128function reduce(promises, fn, initialValue, _each) {
129    if (typeof fn !== "function") {
130        return apiRejection("expecting a function but got " + util.classString(fn));
131    }
132    var array = new ReductionPromiseArray(promises, fn, initialValue, _each);
133    return array.promise();
134}
135
136function gotAccum(accum) {
137    this.accum = accum;
138    this.array._gotAccum(accum);
139    var value = tryConvertToPromise(this.value, this.array._promise);
140    if (value instanceof Promise) {
141        this.array._currentCancellable = value;
142        return value._then(gotValue, undefined, undefined, this, undefined);
143    } else {
144        return gotValue.call(this, value);
145    }
146}
147
148function gotValue(value) {
149    var array = this.array;
150    var promise = array._promise;
151    var fn = tryCatch(array._fn);
152    promise._pushContext();
153    var ret;
154    if (array._eachValues !== undefined) {
155        ret = fn.call(promise._boundValue(), value, this.index, this.length);
156    } else {
157        ret = fn.call(promise._boundValue(),
158                              this.accum, value, this.index, this.length);
159    }
160    if (ret instanceof Promise) {
161        array._currentCancellable = ret;
162    }
163    var promiseCreated = promise._popContext();
164    debug.checkForgottenReturns(
165        ret,
166        promiseCreated,
167        array._eachValues !== undefined ? "Promise.each" : "Promise.reduce",
168        promise
169    );
170    return ret;
171}
172};
173