Skip to content

Commit e61c218

Browse files
committed
Merge branch 'release/v2.0.0-rc1'
2 parents 02f97a0 + d6621b3 commit e61c218

6 files changed

+348
-20
lines changed

Diff for: README.md

+68-14
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
33

44

5-
- [Promise Class 2.0.0-dev1](#promise-class-200-dev1)
5+
- [Promise Class 2.0.0-rc1](#promise-class-200-rc1)
66
- [Usage](#usage)
77
- [Promise()](#promise)
88
- [.then()](#then)
99
- [.fail()](#fail)
1010
- [.finally()](#finally)
1111
- [Promise.loop()](#promiseloop)
1212
- [Promise.serial()](#promiseserial)
13+
- [Promise.parallel()](#promiseparallel)
14+
- [Promise.first()](#promisefirst)
1315
- [Example](#example)
1416
- [Testing](#testing)
1517
- [TL;DR](#tldr)
@@ -23,7 +25,7 @@
2325

2426
[![Build Status](https://travis-ci.org/electricimp/Promise.svg?branch=develop)](https://travis-ci.org/electricimp/Promise)
2527

26-
# Promise Class 2.0.0-dev1
28+
# Promise Class 2.0.0-rc1
2729

2830
This Promise class is based on the PromiseJS definition at:
2931
https://www.promisejs.org/implementing/
@@ -40,9 +42,9 @@ with any sort of detectible failure. Usually, an instantiated Promise object is
4042
returned from a class instead of offering direct callback functions. This uniform
4143
implementation makes the code clearer and easier to read.
4244

43-
**To add this library to your project, add `#require "promise.class.nut:2.0.0-dev1"` to the top of your device code.**
45+
**To add this library to your project, add `#require "promise.class.nut:2.0.0-rc1"` to the top of your device code.**
4446

45-
You can view the library's source code on [GitHub](https://github.com/electricimp/Promise/tree/v2.0.0-dev1).
47+
You can view the library's source code on [GitHub](https://github.com/electricimp/Promise/tree/v2.0.0-rc1).
4648

4749
## Usage
4850

@@ -72,13 +74,17 @@ This function allows the developer to provide a function that is executed once t
7274

7375
### Promise.loop()
7476

75-
`Promise.loop(compareFunction, nextFunction)`
77+
`Promise.loop(continueFunction, nextFunction)`
7678

7779
A way to perform while loops with asynchronous processes.
7880

79-
Stops on `compareFunction() == false` or first rejection of looped _Promise_'s.
81+
Parameters:
82+
- `continueFunction` – function that returns `true` to continue loop or `false` to stop
83+
- `nextFunction` – function that returns next _Promise_ in the loop
8084

81-
Returns _Promise_ that is resolved/rejected with the last value that come from looped _Promise_ when loop finishes.
85+
Stops on `continueFunction() == false` or first rejection of looped _Promise_'s.
86+
87+
Returns _Promise_ that is resolved/rejected with the last value that comes from looped _Promise_ when loop finishes.
8288

8389
For example in the following code `p` resolves with value "counter is 3" in 9 seconds.
8490

@@ -96,23 +102,71 @@ local p = Promise.loop(
96102
);
97103
```
98104

99-
100105
### Promise.serial()
101106

102-
`Promise.serial(promises)`
107+
`Promise.serial(series)`
103108

104109
Returns _Promise_ that resolves when all promises in chain resolve or when the first one rejects.
105110

106-
For example in the following code `p` rejects with value "2" in 2 seconds:
111+
Parameters:
112+
- `series` – array of _Promises_/functions that return promises.
113+
114+
For example in the following code `p` rejects with value "2" in 2.5 seconds:
107115

108116
```squirrel
109-
local promises = [
117+
local series = [
110118
Promise(@(resolve, reject) imp.wakeup(1, @() resolve(1))),
111-
Promise(@(resolve, reject) imp.wakeup(1, @() reject(2))),
112-
Promise(@(resolve, reject) imp.wakeup(1, @() resolve(3)))
119+
@() Promise(@(resolve, reject) imp.wakeup(1.5, @() reject(2))),
120+
Promise(@(resolve, reject) imp.wakeup(0.5, @() resolve(3)))
121+
];
122+
123+
local p = Promise.serial(series);
124+
```
125+
126+
### Promise.parallel()
127+
128+
`Promise.parallel(series)`
129+
130+
Execute Promises in parallel and resolve when they are all done.
131+
Returns Promise that resolves with last paralleled Promise value or rejects with first rejected paralleled Promise value.
132+
133+
Parameters:
134+
- `series` – array of _Promises_/functions that return promises.
135+
136+
For example in the following code `p` resolves with value "2" in 1.5 seconds:
137+
138+
```squirrel
139+
local series = [
140+
@() Promise(@(resolve, reject) imp.wakeup(1, @() resolve(1))),
141+
@() Promise(@(resolve, reject) imp.wakeup(1.5, @() resolve(2))),
142+
Promise(@(resolve, reject) imp.wakeup(0.5, @() resolve(3)))
143+
];
144+
145+
local p = Promise.parallel(series);
146+
```
147+
148+
### Promise.first()
149+
150+
`Promise.first(series)`
151+
152+
Execute Promises in parallel and resolve when the first is done.
153+
Returns Promise that resolves/rejects with the first resolved/rejected Promise value.
154+
155+
Parameters:
156+
- `series` – array of _Promises_/functions that return promises.
157+
158+
For example in the following code `p` rejects with value "1" in 1 second:
159+
160+
```squirrel
161+
local promises = [
162+
// rejects first as the other one with 1s timeout
163+
// starts later from inside .first()
164+
::Promise(function (resolve, reject) { imp.wakeup(1, @() reject(1)) }),
165+
@() ::Promise(function (resolve, reject) { imp.wakeup(1.5, @() resolve(2)) }),
166+
@() ::Promise(function (resolve, reject) { imp.wakeup(1, @() reject(3)) }),
113167
];
114168
115-
local p = Promise.serial(promises);
169+
local p = Promise.parallel(series);
116170
```
117171

118172
## Example

Diff for: promise.class.nut

+67-4
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
* @see https://www.promisejs.org/implementing/
66
* @author Aron Steg
77
* @author Mikhail Yurasov <mikhail@electricimp.com>
8-
* @version 2.0.0-dev
8+
* @version 2.0.0-rc1
99
*/
1010
class Promise {
1111

12-
static version = [2, 0, 0, "dev1"];
12+
static version = [2, 0, 0, "rc1"];
1313

1414
_state = null;
1515
_value = null;
@@ -187,14 +187,77 @@ class Promise {
187187
* all promises in chain resolve:
188188
* one after each other
189189
*
190-
* @param {Promise[]} promises - array of Promises
190+
* @param {{Promise|function}[]} promises - array of Promises/functions that return Promises
191191
* @return {Promise} Promise that is resolved/rejected with the last value that come from looped promise
192192
*/
193193
static function serial(promises) {
194194
local i = 0;
195195
return this.loop(
196196
@() i < promises.len(),
197-
@() promises[i++]
197+
function () {
198+
return "function" == type(promises[i])
199+
? promises[i++]()
200+
: promises[i++];
201+
}
198202
)
199203
}
204+
205+
/**
206+
* Execute Promises in parallel.
207+
*
208+
* @param {{Primise|functiuon}[]} promises
209+
* @param {wait} wait - wait for all promises to finish?
210+
* @returns {Promise}
211+
*/
212+
static function _parallel(promises, wait) {
213+
return (this)(function (resolve, reject) {
214+
local resolved = 0;
215+
216+
local checkDone = function(v = null) {
217+
if ((!wait && resolved == 1) || (wait && resolved == promises.len())) {
218+
resolve(v);
219+
return true;
220+
}
221+
}
222+
223+
if (!checkDone()) {
224+
for (local i = 0; i < promises.len(); i++) {
225+
(
226+
"function" == type(promises[i])
227+
? promises[i]()
228+
: promises[i]
229+
)
230+
.then(function (v) {
231+
resolved++;
232+
checkDone(v);
233+
}, reject);
234+
}
235+
}
236+
237+
}.bindenv(this));
238+
}
239+
240+
/**
241+
* Execute Promises in parallel and resolve when they are all done.
242+
* Returns Promise that resolves with last paralleled Promise value
243+
* or rejects with first rejected paralleled Promise value.
244+
*
245+
* @param {{Primise|functiuon}[]} promises
246+
* @returns {Promise}
247+
*/
248+
static function parallel(promises) {
249+
return this._parallel(promises, true);
250+
}
251+
252+
/**
253+
* Execute Promises in parallel and resolve when the first is done.
254+
* Returns Promise that resolves/rejects with the first
255+
* resolved/rejected Promise value.
256+
*
257+
* @param {{Primise|functiuon}[]} promises
258+
* @returns {Promise}
259+
*/
260+
static function first(promises) {
261+
return this._parallel(promises, false);
262+
}
200263
}

Diff for: tests/First.agent.test.nut

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* "Promise" symbol is injected dependency from ImpUnit_Promise module,
3+
* while class being tested can be accessed from global scope as "::Promise".
4+
*/
5+
6+
/**
7+
* Test case for Promise.first()
8+
*/
9+
class FirstTestCase extends ImpTestCase {
10+
/**
11+
* Check return type
12+
*/
13+
function testReturnType() {
14+
local p = ::Promise.first([]);
15+
this.assertTrue(p instanceof ::Promise);
16+
}
17+
18+
19+
/**
20+
* Test .first() with empty array
21+
*/
22+
function testEmptyPromisesArray() {
23+
return Promise(function(ok, err) {
24+
local p = ::Promise.first([]);
25+
p
26+
.then(@() err("When empty array is passed to .first(), returned promise should not resolve"))
27+
.fail(@() err("When empty array is passed to .first(), returned promise should not reject"));
28+
imp.wakeup(0, ok);
29+
}.bindenv(this));
30+
}
31+
32+
/**
33+
* Test .first() with all Promises in the chain resolving
34+
*/
35+
function testFirstWithAllResolving() {
36+
return Promise(function(ok, err) {
37+
38+
local promises = [
39+
// resolves first as the other one with 0s timeout value
40+
// starts later from inside .first()
41+
::Promise(function (resolve, reject) { imp.wakeup(0, @() resolve(1)) }),
42+
@() ::Promise(function (resolve, reject) { imp.wakeup(0.5, @() resolve(2)) }),
43+
@() ::Promise(function (resolve, reject) { imp.wakeup(0, @() resolve(3)) }),
44+
];
45+
46+
::Promise.first(promises)
47+
.then(function (v) {
48+
try {
49+
// .first() should resolve with value of the first resolved promise
50+
this.assertEqual(1, v);
51+
ok();
52+
} catch (e) {
53+
err(e);
54+
}
55+
}.bindenv(this));
56+
57+
}.bindenv(this));
58+
}
59+
60+
/**
61+
* Test .first() with rejection
62+
*/
63+
function testFirstWithRejection() {
64+
return Promise(function(ok, err) {
65+
66+
local promises = [
67+
// rejects first as the other one with 0s timeout value
68+
// starts later from inside .first()
69+
::Promise(function (resolve, reject) { imp.wakeup(0, @() reject(1)) }),
70+
@() ::Promise(function (resolve, reject) { imp.wakeup(0.5, @() resolve(2)) }),
71+
@() ::Promise(function (resolve, reject) { imp.wakeup(0, @() reject(3)) }),
72+
];
73+
74+
::Promise.first(promises)
75+
76+
.then(@(v) err(".then() should not be called on .first Promise rejection"))
77+
78+
.fail(function (v) {
79+
try {
80+
// .first() should reject with value of the first rejected promise
81+
this.assertEqual(1, v);
82+
ok();
83+
} catch (e) {
84+
err(e);
85+
}
86+
}.bindenv(this));
87+
88+
}.bindenv(this));
89+
}
90+
}

0 commit comments

Comments
 (0)