promise_generator_test.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. // Licensed to the Software Freedom Conservancy (SFC) under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. The SFC licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing,
  12. // software distributed under the License is distributed on an
  13. // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. // KIND, either express or implied. See the License for the
  15. // specific language governing permissions and limitations
  16. // under the License.
  17. 'use strict';
  18. const assert = require('assert');
  19. const promise = require('../../lib/promise');
  20. const {enablePromiseManager, promiseManagerSuite} = require('../../lib/test/promise');
  21. describe('promise.consume()', function() {
  22. promiseManagerSuite(() => {
  23. it('requires inputs to be generator functions', function() {
  24. assert.throws(function() {
  25. promise.consume(function() {});
  26. });
  27. });
  28. it('handles a basic generator with no yielded promises', function() {
  29. var values = [];
  30. return promise.consume(function* () {
  31. var i = 0;
  32. while (i < 4) {
  33. i = yield i + 1;
  34. values.push(i);
  35. }
  36. }).then(function() {
  37. assert.deepEqual([1, 2, 3, 4], values);
  38. });
  39. });
  40. it('handles a promise yielding generator', function() {
  41. var values = [];
  42. return promise.consume(function* () {
  43. var i = 0;
  44. while (i < 4) {
  45. // Test that things are actually async here.
  46. setTimeout(function() {
  47. values.push(i * 2);
  48. }, 10);
  49. yield promise.delayed(10).then(function() {
  50. values.push(i++);
  51. });
  52. }
  53. }).then(function() {
  54. assert.deepEqual([0, 0, 2, 1, 4, 2, 6, 3], values);
  55. });
  56. });
  57. it('assignments to yielded promises get fulfilled value', function() {
  58. return promise.consume(function* () {
  59. let x = yield Promise.resolve(2);
  60. assert.equal(2, x);
  61. });
  62. });
  63. it('uses final return value as fulfillment value', function() {
  64. return promise.consume(function* () {
  65. yield 1;
  66. yield 2;
  67. return 3;
  68. }).then(function(value) {
  69. assert.equal(3, value);
  70. });
  71. });
  72. it('throws rejected promise errors within the generator', function() {
  73. var values = [];
  74. return promise.consume(function* () {
  75. values.push('a');
  76. var e = Error('stub error');
  77. try {
  78. yield Promise.reject(e);
  79. values.push('b');
  80. } catch (ex) {
  81. assert.equal(e, ex);
  82. values.push('c');
  83. }
  84. values.push('d');
  85. }).then(function() {
  86. assert.deepEqual(['a', 'c', 'd'], values);
  87. });
  88. });
  89. it('aborts the generator if there is an unhandled rejection', function() {
  90. var values = [];
  91. var e = Error('stub error');
  92. return promise.consume(function* () {
  93. values.push(1);
  94. yield promise.rejected(e);
  95. values.push(2);
  96. }).catch(function() {
  97. assert.deepEqual([1], values);
  98. });
  99. });
  100. it('yield waits for promises', function() {
  101. let values = [];
  102. let blocker = promise.delayed(100).then(() => {
  103. assert.deepEqual([1], values);
  104. return 2;
  105. });
  106. return promise.consume(function* () {
  107. values.push(1);
  108. values.push(yield blocker, 3);
  109. }).then(function() {
  110. assert.deepEqual([1, 2, 3], values);
  111. });
  112. });
  113. it('accepts custom scopes', function() {
  114. return promise.consume(function* () {
  115. return this.name;
  116. }, {name: 'Bob'}).then(function(value) {
  117. assert.equal('Bob', value);
  118. });
  119. });
  120. it('accepts initial generator arguments', function() {
  121. return promise.consume(function* (a, b) {
  122. assert.equal('red', a);
  123. assert.equal('apples', b);
  124. }, null, 'red', 'apples');
  125. });
  126. });
  127. enablePromiseManager(() => {
  128. it('is possible to cancel promise generators', function() {
  129. var values = [];
  130. var p = promise.consume(function* () {
  131. var i = 0;
  132. while (i < 3) {
  133. yield promise.delayed(100).then(function() {
  134. values.push(i++);
  135. });
  136. }
  137. });
  138. return promise.delayed(75).then(function() {
  139. p.cancel();
  140. return p.catch(function() {
  141. return promise.delayed(300);
  142. });
  143. }).then(function() {
  144. assert.deepEqual([0], values);
  145. });
  146. });
  147. it('executes generator within the control flow', function() {
  148. var promises = [
  149. promise.defer(),
  150. promise.defer()
  151. ];
  152. var values = [];
  153. setTimeout(function() {
  154. assert.deepEqual([], values);
  155. promises[0].fulfill(1);
  156. }, 100);
  157. setTimeout(function() {
  158. assert.deepEqual([1], values);
  159. promises[1].fulfill(2);
  160. }, 200);
  161. return promise.controlFlow().execute(function* () {
  162. values.push(yield promises[0].promise);
  163. values.push(yield promises[1].promise);
  164. values.push('fin');
  165. }).then(function() {
  166. assert.deepEqual([1, 2, 'fin'], values);
  167. });
  168. });
  169. it('handles tasks scheduled in generator', function() {
  170. var flow = promise.controlFlow();
  171. return flow.execute(function* () {
  172. var x = yield flow.execute(function() {
  173. return promise.delayed(10).then(function() {
  174. return 1;
  175. });
  176. });
  177. var y = yield flow.execute(function() {
  178. return 2;
  179. });
  180. return x + y;
  181. }).then(function(value) {
  182. assert.equal(3, value);
  183. });
  184. });
  185. it('blocks the control flow while processing generator', function() {
  186. var values = [];
  187. return promise.controlFlow().wait(function* () {
  188. yield values.push(1);
  189. values.push(yield promise.delayed(10).then(function() {
  190. return 2;
  191. }));
  192. yield values.push(3);
  193. return values.length === 6;
  194. }, 250).then(function() {
  195. assert.deepEqual([1, 2, 3, 1, 2, 3], values);
  196. });
  197. });
  198. it('ControlFlow.wait() will timeout on long generator', function() {
  199. var values = [];
  200. return promise.controlFlow().wait(function* () {
  201. var i = 0;
  202. while (i < 3) {
  203. yield promise.delayed(100).then(function() {
  204. values.push(i++);
  205. });
  206. }
  207. }, 75).catch(function() {
  208. assert.deepEqual(
  209. [0, 1, 2], values, 'Should complete one loop of wait condition');
  210. });
  211. });
  212. describe('generators in promise callbacks', function() {
  213. it('works with no initial value', function() {
  214. var promises = [
  215. promise.defer(),
  216. promise.defer()
  217. ];
  218. var values = [];
  219. setTimeout(function() {
  220. promises[0].fulfill(1);
  221. }, 50);
  222. setTimeout(function() {
  223. promises[1].fulfill(2);
  224. }, 100);
  225. return promise.fulfilled().then(function*() {
  226. values.push(yield promises[0].promise);
  227. values.push(yield promises[1].promise);
  228. values.push('fin');
  229. }).then(function() {
  230. assert.deepEqual([1, 2, 'fin'], values);
  231. });
  232. });
  233. it('starts the generator with promised value', function() {
  234. var promises = [
  235. promise.defer(),
  236. promise.defer()
  237. ];
  238. var values = [];
  239. setTimeout(function() {
  240. promises[0].fulfill(1);
  241. }, 50);
  242. setTimeout(function() {
  243. promises[1].fulfill(2);
  244. }, 100);
  245. return promise.fulfilled(3).then(function*(value) {
  246. var p1 = yield promises[0].promise;
  247. var p2 = yield promises[1].promise;
  248. values.push(value + p1);
  249. values.push(value + p2);
  250. values.push('fin');
  251. }).then(function() {
  252. assert.deepEqual([4, 5, 'fin'], values);
  253. });
  254. });
  255. it('throws yielded rejections within the generator callback', function() {
  256. var d = promise.defer();
  257. var e = Error('stub');
  258. setTimeout(function() {
  259. d.reject(e);
  260. }, 50);
  261. return promise.fulfilled().then(function*() {
  262. var threw = false;
  263. try {
  264. yield d.promise;
  265. } catch (ex) {
  266. threw = true;
  267. assert.equal(e, ex);
  268. }
  269. assert.ok(threw);
  270. });
  271. });
  272. });
  273. });
  274. });