Skip to content

Commit e8c20be

Browse files
committed
Require arrays as input for promise-array related functions (closes #35)
1 parent d22b960 commit e8c20be

8 files changed

Lines changed: 109 additions & 462 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ CHANGELOG
88
* Introduce a global task queue to eliminate recursion and reduce stack size
99
when chaining promises.
1010
* BC break: The progression API has been removed (#32).
11+
* BC break: The promise-array related functions (`all()`, `race()`, `any()`,
12+
`some()`, `map()`, `reduce()`) now require an array of promises or values
13+
as input. Before, arrays and promises which resolve to an array were
14+
supported, other input types resolved to empty arrays or `null`. (#35).
1115

1216
* 2.4.0 (2016-03-31)
1317

src/functions.php

Lines changed: 105 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -34,191 +34,168 @@ function reject($promiseOrValue = null)
3434
return new RejectedPromise($promiseOrValue);
3535
}
3636

37-
function all($promisesOrValues)
37+
function all(array $promisesOrValues)
3838
{
3939
return map($promisesOrValues, function ($val) {
4040
return $val;
4141
});
4242
}
4343

44-
function race($promisesOrValues)
44+
function race(array $promisesOrValues)
4545
{
46+
if (!$promisesOrValues) {
47+
return resolve();
48+
}
49+
4650
$cancellationQueue = new CancellationQueue();
47-
$cancellationQueue->enqueue($promisesOrValues);
4851

4952
return new Promise(function ($resolve, $reject) use ($promisesOrValues, $cancellationQueue) {
50-
resolve($promisesOrValues)
51-
->done(function ($array) use ($cancellationQueue, $resolve, $reject) {
52-
if (!is_array($array) || !$array) {
53-
$resolve();
54-
return;
55-
}
53+
$fulfiller = function ($value) use ($cancellationQueue, $resolve) {
54+
$cancellationQueue();
55+
$resolve($value);
56+
};
5657

57-
$fulfiller = function ($value) use ($cancellationQueue, $resolve) {
58-
$cancellationQueue();
59-
$resolve($value);
60-
};
58+
$rejecter = function ($reason) use ($cancellationQueue, $reject) {
59+
$cancellationQueue();
60+
$reject($reason);
61+
};
6162

62-
$rejecter = function ($reason) use ($cancellationQueue, $reject) {
63-
$cancellationQueue();
64-
$reject($reason);
65-
};
63+
foreach ($promisesOrValues as $promiseOrValue) {
64+
$cancellationQueue->enqueue($promiseOrValue);
6665

67-
foreach ($array as $promiseOrValue) {
68-
$cancellationQueue->enqueue($promiseOrValue);
69-
70-
resolve($promiseOrValue)
71-
->done($fulfiller, $rejecter);
72-
}
73-
}, $reject);
66+
resolve($promiseOrValue)
67+
->done($fulfiller, $rejecter);
68+
}
7469
}, $cancellationQueue);
7570
}
7671

77-
function any($promisesOrValues)
72+
function any(array $promisesOrValues)
7873
{
7974
return some($promisesOrValues, 1)
8075
->then(function ($val) {
8176
return array_shift($val);
8277
});
8378
}
8479

85-
function some($promisesOrValues, $howMany)
80+
function some(array $promisesOrValues, $howMany)
8681
{
87-
$cancellationQueue = new CancellationQueue();
88-
$cancellationQueue->enqueue($promisesOrValues);
82+
if ($howMany < 1) {
83+
return resolve([]);
84+
}
8985

90-
return new Promise(function ($resolve, $reject) use ($promisesOrValues, $howMany, $cancellationQueue) {
91-
resolve($promisesOrValues)
92-
->done(function ($array) use ($howMany, $cancellationQueue, $resolve, $reject) {
93-
if (!is_array($array) || $howMany < 1) {
94-
$resolve([]);
95-
return;
96-
}
86+
$len = count($promisesOrValues);
87+
88+
if ($len < $howMany) {
89+
return reject(
90+
new Exception\LengthException(
91+
sprintf(
92+
'Input array must contain at least %d item%s but contains only %s item%s.',
93+
$howMany,
94+
1 === $howMany ? '' : 's',
95+
$len,
96+
1 === $len ? '' : 's'
97+
)
98+
)
99+
);
100+
}
97101

98-
$len = count($array);
99-
100-
if ($len < $howMany) {
101-
throw new Exception\LengthException(
102-
sprintf(
103-
'Input array must contain at least %d item%s but contains only %s item%s.',
104-
$howMany,
105-
1 === $howMany ? '' : 's',
106-
$len,
107-
1 === $len ? '' : 's'
108-
)
109-
);
110-
}
102+
$cancellationQueue = new CancellationQueue();
111103

112-
$toResolve = $howMany;
113-
$toReject = ($len - $toResolve) + 1;
114-
$values = [];
115-
$reasons = [];
104+
return new Promise(function ($resolve, $reject) use ($len, $promisesOrValues, $howMany, $cancellationQueue) {
105+
$toResolve = $howMany;
106+
$toReject = ($len - $toResolve) + 1;
107+
$values = [];
108+
$reasons = [];
116109

117-
foreach ($array as $i => $promiseOrValue) {
118-
$fulfiller = function ($val) use ($i, &$values, &$toResolve, $toReject, $resolve, $cancellationQueue) {
119-
if ($toResolve < 1 || $toReject < 1) {
120-
return;
121-
}
110+
foreach ($promisesOrValues as $i => $promiseOrValue) {
111+
$fulfiller = function ($val) use ($i, &$values, &$toResolve, $toReject, $resolve, $cancellationQueue) {
112+
if ($toResolve < 1 || $toReject < 1) {
113+
return;
114+
}
122115

123-
$values[$i] = $val;
116+
$values[$i] = $val;
124117

125-
if (0 === --$toResolve) {
126-
$cancellationQueue();
127-
$resolve($values);
128-
}
129-
};
118+
if (0 === --$toResolve) {
119+
$cancellationQueue();
120+
$resolve($values);
121+
}
122+
};
130123

131-
$rejecter = function ($reason) use ($i, &$reasons, &$toReject, $toResolve, $reject) {
132-
if ($toResolve < 1 || $toReject < 1) {
133-
return;
134-
}
124+
$rejecter = function ($reason) use ($i, &$reasons, &$toReject, $toResolve, $reject) {
125+
if ($toResolve < 1 || $toReject < 1) {
126+
return;
127+
}
135128

136-
$reasons[$i] = $reason;
129+
$reasons[$i] = $reason;
137130

138-
if (0 === --$toReject) {
139-
$reject($reasons);
140-
}
141-
};
131+
if (0 === --$toReject) {
132+
$reject($reasons);
133+
}
134+
};
142135

143-
$cancellationQueue->enqueue($promiseOrValue);
136+
$cancellationQueue->enqueue($promiseOrValue);
144137

145-
resolve($promiseOrValue)
146-
->done($fulfiller, $rejecter);
147-
}
148-
}, $reject);
138+
resolve($promiseOrValue)
139+
->done($fulfiller, $rejecter);
140+
}
149141
}, $cancellationQueue);
150142
}
151143

152-
function map($promisesOrValues, callable $mapFunc)
144+
function map(array $promisesOrValues, callable $mapFunc)
153145
{
146+
if (!$promisesOrValues) {
147+
return resolve([]);
148+
}
149+
154150
$cancellationQueue = new CancellationQueue();
155-
$cancellationQueue->enqueue($promisesOrValues);
156151

157152
return new Promise(function ($resolve, $reject) use ($promisesOrValues, $mapFunc, $cancellationQueue) {
158-
resolve($promisesOrValues)
159-
->done(function ($array) use ($mapFunc, $cancellationQueue, $resolve, $reject) {
160-
if (!is_array($array) || !$array) {
161-
$resolve([]);
162-
return;
163-
}
164-
165-
$toResolve = count($array);
166-
$values = [];
153+
$toResolve = count($promisesOrValues);
154+
$values = [];
167155

168-
foreach ($array as $i => $promiseOrValue) {
169-
$cancellationQueue->enqueue($promiseOrValue);
156+
foreach ($promisesOrValues as $i => $promiseOrValue) {
157+
$cancellationQueue->enqueue($promiseOrValue);
170158

171-
resolve($promiseOrValue)
172-
->then($mapFunc)
173-
->done(
174-
function ($mapped) use ($i, &$values, &$toResolve, $resolve) {
175-
$values[$i] = $mapped;
159+
resolve($promiseOrValue)
160+
->then($mapFunc)
161+
->done(
162+
function ($mapped) use ($i, &$values, &$toResolve, $resolve) {
163+
$values[$i] = $mapped;
176164

177-
if (0 === --$toResolve) {
178-
$resolve($values);
179-
}
180-
},
181-
$reject
182-
);
183-
}
184-
}, $reject);
165+
if (0 === --$toResolve) {
166+
$resolve($values);
167+
}
168+
},
169+
$reject
170+
);
171+
}
185172
}, $cancellationQueue);
186173
}
187174

188-
function reduce($promisesOrValues, callable $reduceFunc, $initialValue = null)
175+
function reduce(array $promisesOrValues, callable $reduceFunc, $initialValue = null)
189176
{
190177
$cancellationQueue = new CancellationQueue();
191-
$cancellationQueue->enqueue($promisesOrValues);
192178

193179
return new Promise(function ($resolve, $reject) use ($promisesOrValues, $reduceFunc, $initialValue, $cancellationQueue) {
194-
resolve($promisesOrValues)
195-
->done(function ($array) use ($reduceFunc, $initialValue, $cancellationQueue, $resolve, $reject) {
196-
if (!is_array($array)) {
197-
$array = [];
198-
}
199-
200-
$total = count($array);
201-
$i = 0;
180+
$total = count($promisesOrValues);
181+
$i = 0;
202182

203-
// Wrap the supplied $reduceFunc with one that handles promises and then
204-
// delegates to the supplied.
205-
$wrappedReduceFunc = function ($current, $val) use ($reduceFunc, $cancellationQueue, $total, &$i) {
206-
$cancellationQueue->enqueue($val);
183+
$wrappedReduceFunc = function ($current, $val) use ($reduceFunc, $cancellationQueue, $total, &$i) {
184+
$cancellationQueue->enqueue($val);
207185

208-
return $current
209-
->then(function ($c) use ($reduceFunc, $total, &$i, $val) {
210-
return resolve($val)
211-
->then(function ($value) use ($reduceFunc, $total, &$i, $c) {
212-
return $reduceFunc($c, $value, $i++, $total);
213-
});
186+
return $current
187+
->then(function ($c) use ($reduceFunc, $total, &$i, $val) {
188+
return resolve($val)
189+
->then(function ($value) use ($reduceFunc, $total, &$i, $c) {
190+
return $reduceFunc($c, $value, $i++, $total);
214191
});
215-
};
192+
});
193+
};
216194

217-
$cancellationQueue->enqueue($initialValue);
195+
$cancellationQueue->enqueue($initialValue);
218196

219-
array_reduce($array, $wrappedReduceFunc, resolve($initialValue))
220-
->done($resolve, $reject);
221-
}, $reject);
197+
array_reduce($promisesOrValues, $wrappedReduceFunc, resolve($initialValue))
198+
->done($resolve, $reject);
222199
}, $cancellationQueue);
223200
}
224201

tests/FunctionAllTest.php

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -68,30 +68,4 @@ public function shouldRejectIfAnyInputPromiseRejects()
6868
all([resolve(1), reject(2), resolve(3)])
6969
->then($this->expectCallableNever(), $mock);
7070
}
71-
72-
/** @test */
73-
public function shouldAcceptAPromiseForAnArray()
74-
{
75-
$mock = $this->createCallableMock();
76-
$mock
77-
->expects($this->once())
78-
->method('__invoke')
79-
->with($this->identicalTo([1, 2, 3]));
80-
81-
all(resolve([1, 2, 3]))
82-
->then($mock);
83-
}
84-
85-
/** @test */
86-
public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray()
87-
{
88-
$mock = $this->createCallableMock();
89-
$mock
90-
->expects($this->once())
91-
->method('__invoke')
92-
->with($this->identicalTo([]));
93-
94-
all(resolve(1))
95-
->then($mock);
96-
}
9771
}

0 commit comments

Comments
 (0)