• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {
2  isArray,
3  isMaybeThenable
4} from './utils';
5import {
6  noop,
7  reject,
8  fulfill,
9  subscribe,
10  FULFILLED,
11  REJECTED,
12  PENDING,
13  handleMaybeThenable
14} from './-internal';
15
16import then from './then';
17import Promise from './promise';
18import originalResolve from './promise/resolve';
19import originalThen from './then';
20import { makePromise, PROMISE_ID } from './-internal';
21
22function validationError() {
23  return new Error('Array Methods must be provided an Array');
24};
25
26export default class Enumerator {
27  constructor(Constructor, input) {
28    this._instanceConstructor = Constructor;
29    this.promise = new Constructor(noop);
30
31    if (!this.promise[PROMISE_ID]) {
32      makePromise(this.promise);
33    }
34
35    if (isArray(input)) {
36      this.length     = input.length;
37      this._remaining = input.length;
38
39      this._result = new Array(this.length);
40
41      if (this.length === 0) {
42        fulfill(this.promise, this._result);
43      } else {
44        this.length = this.length || 0;
45        this._enumerate(input);
46        if (this._remaining === 0) {
47          fulfill(this.promise, this._result);
48        }
49      }
50    } else {
51      reject(this.promise, validationError());
52    }
53  }
54  _enumerate(input) {
55    for (let i = 0; this._state === PENDING && i < input.length; i++) {
56      this._eachEntry(input[i], i);
57    }
58  }
59
60  _eachEntry(entry, i) {
61    let c = this._instanceConstructor;
62    let { resolve } = c;
63
64    if (resolve === originalResolve) {
65      let then;
66      let error;
67      let didError = false;
68      try {
69        then = entry.then;
70      } catch (e) {
71        didError = true;
72        error = e;
73      }
74
75      if (then === originalThen &&
76        entry._state !== PENDING) {
77        this._settledAt(entry._state, i, entry._result);
78      } else if (typeof then !== 'function') {
79        this._remaining--;
80        this._result[i] = entry;
81      } else if (c === Promise) {
82        let promise = new c(noop);
83        if (didError) {
84          reject(promise, error);
85        } else {
86          handleMaybeThenable(promise, entry, then);
87        }
88        this._willSettleAt(promise, i);
89      } else {
90        this._willSettleAt(new c(resolve => resolve(entry)), i);
91      }
92    } else {
93      this._willSettleAt(resolve(entry), i);
94    }
95  }
96
97  _settledAt(state, i, value) {
98    let { promise } = this;
99
100    if (promise._state === PENDING) {
101      this._remaining--;
102
103      if (state === REJECTED) {
104        reject(promise, value);
105      } else {
106        this._result[i] = value;
107      }
108    }
109
110    if (this._remaining === 0) {
111      fulfill(promise, this._result);
112    }
113  }
114
115  _willSettleAt(promise, i) {
116    let enumerator = this;
117
118    subscribe(
119      promise, undefined,
120      value => enumerator._settledAt(FULFILLED, i, value),
121      reason => enumerator._settledAt(REJECTED, i, reason)
122    );
123  }
124};
125