promises.js 27.6 KB
Newer Older
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

28
// Flags: --allow-natives-syntax --promise-extra
rossberg@chromium.org's avatar
rossberg@chromium.org committed
29

30 31
// Make sure we don't rely on functions patchable by monkeys.
var call = Function.prototype.call.call.bind(Function.prototype.call)
32 33
var getOwnPropertyNames = Object.getOwnPropertyNames;
var defineProperty = Object.defineProperty;
34 35
var numberPrototype = Number.prototype;
var symbolIterator = Symbol.iterator;
36 37 38 39 40 41 42 43


(function() {
  // Test before clearing global (fails otherwise)
  assertEquals("[object Promise]",
      Object.prototype.toString.call(new Promise(function() {})));
})();

44 45 46 47 48 49

function clear(o) {
  if (o === null || (typeof o !== 'object' && typeof o !== 'function')) return
  clear(o.__proto__)
  var properties = getOwnPropertyNames(o)
  for (var i in properties) {
50 51
    // Do not clobber Object.prototype.toString, which is used by tests.
    if (properties[i] === "toString") continue;
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
    clearProp(o, properties[i])
  }
}

function clearProp(o, name) {
  var poisoned = {caller: 0, callee: 0, arguments: 0}
  try {
    var x = o[name]
    o[name] = undefined
    clear(x)
  } catch(e) {} // assertTrue(name in poisoned) }
}

// Find intrinsics and null them out.
var globals = Object.getOwnPropertyNames(this)
67 68 69 70 71 72 73 74 75
var whitelist = {
  Promise: true,
  TypeError: true,
  String: true,
  JSON: true,
  Error: true,
  MjsUnitAssertionError: true
};

76 77 78 79 80 81 82 83 84
for (var i in globals) {
  var name = globals[i]
  if (name in whitelist || name[0] === name[0].toLowerCase()) delete globals[i]
}
for (var i in globals) {
  if (globals[i]) clearProp(this, globals[i])
}


rossberg@chromium.org's avatar
rossberg@chromium.org committed
85 86 87 88 89 90 91 92 93 94 95 96 97
var asyncAssertsExpected = 0;

function assertAsyncRan() { ++asyncAssertsExpected }

function assertAsync(b, s) {
  if (b) {
    print(s, "succeeded")
  } else {
    %AbortJS(s + " FAILED!")  // Simply throwing here will have no effect.
  }
  --asyncAssertsExpected
}

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
function assertLater(f, name) {
  assertFalse(f()); // should not be true synchronously
  ++asyncAssertsExpected;
  var iterations = 0;
  function runAssertion() {
    if (f()) {
      print(name, "succeeded");
      --asyncAssertsExpected;
    } else if (iterations++ < 10) {
      %EnqueueMicrotask(runAssertion);
    } else {
      %AbortJS(name + " FAILED!");
    }
  }
  %EnqueueMicrotask(runAssertion);
}

rossberg@chromium.org's avatar
rossberg@chromium.org committed
115
function assertAsyncDone(iteration) {
116 117 118 119 120
  var iteration = iteration || 0;
  %EnqueueMicrotask(function() {
    if (asyncAssertsExpected === 0)
      assertAsync(true, "all")
    else if (iteration > 10)  // Shouldn't take more.
121
      assertAsync(false, "all... " + asyncAssertsExpected)
122 123 124
    else
      assertAsyncDone(iteration + 1)
  });
rossberg@chromium.org's avatar
rossberg@chromium.org committed
125 126
}

127 128 129 130 131 132 133 134
(function() {
  assertThrows(function() { Promise(function() {}) }, TypeError)
})();

(function() {
  assertTrue(new Promise(function() {}) instanceof Promise)
})();

rossberg@chromium.org's avatar
rossberg@chromium.org committed
135 136 137 138 139
(function() {
  assertThrows(function() { new Promise(5) }, TypeError)
})();

(function() {
140 141 142 143 144 145 146 147 148
  assertDoesNotThrow(function() { new Promise(function() { throw 5 }) })
})();

(function() {
  (new Promise(function() { throw 5 })).chain(
    assertUnreachable,
    function(r) { assertAsync(r === 5, "new-throw") }
  )
  assertAsyncRan()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
149 150 151
})();

(function() {
152 153
  Promise.accept(5);
  Promise.accept(5).chain(undefined, assertUnreachable).chain(
rossberg@chromium.org's avatar
rossberg@chromium.org committed
154 155 156 157 158 159 160
    function(x) { assertAsync(x === 5, "resolved/chain-nohandler") },
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
161
  Promise.reject(5).chain(assertUnreachable, undefined).chain(
rossberg@chromium.org's avatar
rossberg@chromium.org committed
162 163 164 165 166 167 168
    assertUnreachable,
    function(r) { assertAsync(r === 5, "rejected/chain-nohandler") }
  )
  assertAsyncRan()
})();

(function() {
169
  Promise.accept(5).then(undefined, assertUnreachable).chain(
170 171 172 173
    function(x) { assertAsync(x === 5, "resolved/then-nohandler-undefined") },
    assertUnreachable
  )
  assertAsyncRan()
174
  Promise.accept(6).then(null, assertUnreachable).chain(
175
    function(x) { assertAsync(x === 6, "resolved/then-nohandler-null") },
rossberg@chromium.org's avatar
rossberg@chromium.org committed
176 177 178 179 180 181
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
182
  Promise.reject(5).then(assertUnreachable, undefined).chain(
rossberg@chromium.org's avatar
rossberg@chromium.org committed
183
    assertUnreachable,
184 185 186 187 188 189
    function(r) { assertAsync(r === 5, "rejected/then-nohandler-undefined") }
  )
  assertAsyncRan()
  Promise.reject(6).then(assertUnreachable, null).chain(
    assertUnreachable,
    function(r) { assertAsync(r === 6, "rejected/then-nohandler-null") }
rossberg@chromium.org's avatar
rossberg@chromium.org committed
190 191 192 193 194
  )
  assertAsyncRan()
})();

(function() {
195 196 197
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
198
  // Note: Chain now has then-style semantics, here and in future tests.
rossberg@chromium.org's avatar
rossberg@chromium.org committed
199
  p3.chain(
200
    function(x) { assertAsync(x === 5, "resolved/chain") },
rossberg@chromium.org's avatar
rossberg@chromium.org committed
201 202 203 204 205 206
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
207 208 209
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
210 211 212 213 214 215 216 217
  p3.then(
    function(x) { assertAsync(x === 5, "resolved/then") },
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
218
  var p1 = Promise.reject(5)
219 220
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
221
  p3.chain(
222 223
    assertUnreachable,
    function(x) { assertAsync(x === 5, "rejected/chain") }
rossberg@chromium.org's avatar
rossberg@chromium.org committed
224 225 226 227 228
  )
  assertAsyncRan()
})();

(function() {
229
  var p1 = Promise.reject(5)
230 231
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
232 233 234 235 236 237 238 239
  p3.then(
    assertUnreachable,
    function(x) { assertAsync(x === 5, "rejected/then") }
  )
  assertAsyncRan()
})();

(function() {
240 241 242
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
243
  p3.chain(function(x) { return x }, assertUnreachable).chain(
244
    function(x) { assertAsync(x === 5, "resolved/chain/chain") },
rossberg@chromium.org's avatar
rossberg@chromium.org committed
245 246 247
    assertUnreachable
  )
  assertAsyncRan()
248
})();
rossberg@chromium.org's avatar
rossberg@chromium.org committed
249 250

(function() {
251 252 253
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
254 255 256 257 258 259 260 261
  p3.chain(function(x) { return x }, assertUnreachable).then(
    function(x) { assertAsync(x === 5, "resolved/chain/then") },
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
262 263 264
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
265 266 267 268 269 270 271 272
  p3.chain(function(x) { return 6 }, assertUnreachable).chain(
    function(x) { assertAsync(x === 6, "resolved/chain/chain2") },
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
273 274 275
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
276 277 278 279 280 281 282 283
  p3.chain(function(x) { return 6 }, assertUnreachable).then(
    function(x) { assertAsync(x === 6, "resolved/chain/then2") },
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
284 285 286
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
287 288 289 290 291 292 293 294
  p3.then(function(x) { return x + 1 }, assertUnreachable).chain(
    function(x) { assertAsync(x === 6, "resolved/then/chain") },
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
295 296 297
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
298 299 300 301 302 303 304 305
  p3.then(function(x) { return x + 1 }, assertUnreachable).then(
    function(x) { assertAsync(x === 6, "resolved/then/then") },
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
306 307 308 309
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
  p3.then(function(x){ return Promise.accept(x+1) }, assertUnreachable).chain(
rossberg@chromium.org's avatar
rossberg@chromium.org committed
310 311 312 313 314 315 316
    function(x) { assertAsync(x === 6, "resolved/then/chain2") },
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
317 318 319 320
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
  p3.then(function(x) { return Promise.accept(x+1) }, assertUnreachable).then(
rossberg@chromium.org's avatar
rossberg@chromium.org committed
321 322 323 324 325 326 327
    function(x) { assertAsync(x === 6, "resolved/then/then2") },
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
328 329 330
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
331 332 333 334 335 336 337 338
  p3.chain(function(x) { throw 6 }, assertUnreachable).chain(
    assertUnreachable,
    function(x) { assertAsync(x === 6, "resolved/chain-throw/chain") }
  )
  assertAsyncRan()
})();

(function() {
339 340 341
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
342 343 344 345 346 347 348 349
  p3.chain(function(x) { throw 6 }, assertUnreachable).then(
    assertUnreachable,
    function(x) { assertAsync(x === 6, "resolved/chain-throw/then") }
  )
  assertAsyncRan()
})();

(function() {
350 351 352
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
353 354 355 356 357 358 359 360
  p3.then(function(x) { throw 6 }, assertUnreachable).chain(
    assertUnreachable,
    function(x) { assertAsync(x === 6, "resolved/then-throw/chain") }
  )
  assertAsyncRan()
})();

(function() {
361 362 363
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
364 365 366 367 368 369 370 371
  p3.then(function(x) { throw 6 }, assertUnreachable).then(
    assertUnreachable,
    function(x) { assertAsync(x === 6, "resolved/then-throw/then") }
  )
  assertAsyncRan()
})();

(function() {
372
  var p1 = Promise.accept(5)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
373
  var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
374
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
375
  p3.chain(
376
    function(x) { assertAsync(x === 5, "resolved/thenable/chain") },
rossberg@chromium.org's avatar
rossberg@chromium.org committed
377 378 379 380 381 382
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
383
  var p1 = Promise.accept(5)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
384
  var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
385
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
386 387 388 389 390 391 392 393
  p3.then(
    function(x) { assertAsync(x === 5, "resolved/thenable/then") },
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
394
  var p1 = Promise.reject(5)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
395
  var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
396
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
397
  p3.chain(
398 399
    assertUnreachable,
    function(x) { assertAsync(x === 5, "rejected/thenable/chain") }
rossberg@chromium.org's avatar
rossberg@chromium.org committed
400 401 402 403 404
  )
  assertAsyncRan()
})();

(function() {
405
  var p1 = Promise.reject(5)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
406
  var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
407
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
408 409 410 411 412 413 414 415
  p3.then(
    assertUnreachable,
    function(x) { assertAsync(x === 5, "rejected/thenable/then") }
  )
  assertAsyncRan()
})();

(function() {
416
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
417
  var p1 = deferred.promise
418 419
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
420
  p3.chain(
421
    function(x) { assertAsync(x === 5, "chain/resolve") },
rossberg@chromium.org's avatar
rossberg@chromium.org committed
422 423 424 425 426 427 428
    assertUnreachable
  )
  deferred.resolve(5)
  assertAsyncRan()
})();

(function() {
429
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
430
  var p1 = deferred.promise
431 432
  var p2 = Promise.resolve(p1)
  var p3 = Promise.resolve(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
433 434 435 436 437 438 439 440 441
  p3.then(
    function(x) { assertAsync(x === 5, "then/resolve") },
    assertUnreachable
  )
  deferred.resolve(5)
  assertAsyncRan()
})();

(function() {
442
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
443
  var p1 = deferred.promise
444 445
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
446
  p3.chain(
447 448
    assertUnreachable,
    function(x) { assertAsync(x === 5, "chain/reject") }
rossberg@chromium.org's avatar
rossberg@chromium.org committed
449 450 451 452 453 454
  )
  deferred.reject(5)
  assertAsyncRan()
})();

(function() {
455
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
456
  var p1 = deferred.promise
457 458
  var p2 = Promise.accept(p1)
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
459 460 461 462 463 464 465 466
  p3.then(
    assertUnreachable,
    function(x) { assertAsync(x === 5, "then/reject") }
  )
  deferred.reject(5)
  assertAsyncRan()
})();

467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
(function() {
  var deferred = Promise.defer()
  var p1 = deferred.promise
  var p2 = p1.then(1, 2)
  p2.then(
    function(x) { assertAsync(x === 5, "then/resolve-non-function") },
    assertUnreachable
  )
  deferred.resolve(5)
  assertAsyncRan()
})();

(function() {
  var deferred = Promise.defer()
  var p1 = deferred.promise
  var p2 = p1.then(1, 2)
  p2.then(
    assertUnreachable,
    function(x) { assertAsync(x === 5, "then/reject-non-function") }
  )
  deferred.reject(5)
  assertAsyncRan()
})();

rossberg@chromium.org's avatar
rossberg@chromium.org committed
491
(function() {
492
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
493 494
  var p1 = deferred.promise
  var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
495
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
496
  p3.chain(
497
    function(x) { assertAsync(x === 5, "chain/resolve/thenable") },
rossberg@chromium.org's avatar
rossberg@chromium.org committed
498 499 500 501 502 503 504
    assertUnreachable
  )
  deferred.resolve(5)
  assertAsyncRan()
})();

(function() {
505
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
506 507
  var p1 = deferred.promise
  var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
508
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
509 510 511 512 513 514 515 516 517
  p3.then(
    function(x) { assertAsync(x === 5, "then/resolve/thenable") },
    assertUnreachable
  )
  deferred.resolve(5)
  assertAsyncRan()
})();

(function() {
518
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
519 520
  var p1 = deferred.promise
  var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
521
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
522
  p3.chain(
523 524
    assertUnreachable,
    function(x) { assertAsync(x === 5, "chain/reject/thenable") }
rossberg@chromium.org's avatar
rossberg@chromium.org committed
525 526 527 528 529 530
  )
  deferred.reject(5)
  assertAsyncRan()
})();

(function() {
531
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
532 533
  var p1 = deferred.promise
  var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
534
  var p3 = Promise.accept(p2)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
535 536 537 538 539 540 541 542 543
  p3.then(
    assertUnreachable,
    function(x) { assertAsync(x === 5, "then/reject/thenable") }
  )
  deferred.reject(5)
  assertAsyncRan()
})();

(function() {
544 545
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
546
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
547 548
  var p3 = deferred.promise
  p3.chain(
549
    function(x) { assertAsync(x === 5, "chain/resolve2") },
rossberg@chromium.org's avatar
rossberg@chromium.org committed
550 551 552 553
    assertUnreachable
  )
  deferred.resolve(p2)
  assertAsyncRan()
554
})();
rossberg@chromium.org's avatar
rossberg@chromium.org committed
555 556

(function() {
557 558
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
559
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
560 561 562 563 564 565 566 567 568 569
  var p3 = deferred.promise
  p3.then(
    function(x) { assertAsync(x === 5, "then/resolve2") },
    assertUnreachable
  )
  deferred.resolve(p2)
  assertAsyncRan()
})();

(function() {
570 571
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
572
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
573 574 575 576 577 578 579 580 581 582
  var p3 = deferred.promise
  p3.chain(
    assertUnreachable,
    function(x) { assertAsync(x === 5, "chain/reject2") }
  )
  deferred.reject(5)
  assertAsyncRan()
})();

(function() {
583 584
  var p1 = Promise.accept(5)
  var p2 = Promise.accept(p1)
585
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
586 587 588 589 590 591 592 593 594 595
  var p3 = deferred.promise
  p3.then(
    assertUnreachable,
    function(x) { assertAsync(x === 5, "then/reject2") }
  )
  deferred.reject(5)
  assertAsyncRan()
})();

(function() {
596
  var p1 = Promise.accept(5)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
597
  var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
598
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
599 600
  var p3 = deferred.promise
  p3.chain(
601
    function(x) { assertAsync(x === 5, "chain/resolve/thenable2") },
rossberg@chromium.org's avatar
rossberg@chromium.org committed
602 603 604 605
    assertUnreachable
  )
  deferred.resolve(p2)
  assertAsyncRan()
606
})();
rossberg@chromium.org's avatar
rossberg@chromium.org committed
607 608

(function() {
609
  var p1 = Promise.accept(5)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
610
  var p2 = {then: function(onResolve, onReject) { onResolve(p1) }}
611
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
612 613 614 615 616 617 618 619 620 621
  var p3 = deferred.promise
  p3.then(
    function(x) { assertAsync(x === 5, "then/resolve/thenable2") },
    assertUnreachable
  )
  deferred.resolve(p2)
  assertAsyncRan()
})();

(function() {
622
  var p1 = Promise.accept(0)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
623 624 625 626 627 628 629 630 631
  var p2 = p1.chain(function(x) { return p2 }, assertUnreachable)
  p2.chain(
    assertUnreachable,
    function(r) { assertAsync(r instanceof TypeError, "cyclic/chain") }
  )
  assertAsyncRan()
})();

(function() {
632
  var p1 = Promise.accept(0)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
633 634 635 636 637 638 639 640 641
  var p2 = p1.then(function(x) { return p2 }, assertUnreachable)
  p2.chain(
    assertUnreachable,
    function(r) { assertAsync(r instanceof TypeError, "cyclic/then") }
  )
  assertAsyncRan()
})();

(function() {
642
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
643 644 645
  var p = deferred.promise
  deferred.resolve(p)
  p.chain(
646 647
    assertUnreachable,
    function(r) { assertAsync(r instanceof TypeError, "cyclic/deferred/then") }
rossberg@chromium.org's avatar
rossberg@chromium.org committed
648 649
  )
  assertAsyncRan()
650
})();
rossberg@chromium.org's avatar
rossberg@chromium.org committed
651 652

(function() {
653
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
654 655 656 657 658 659 660
  var p = deferred.promise
  deferred.resolve(p)
  p.then(
    assertUnreachable,
    function(r) { assertAsync(r instanceof TypeError, "cyclic/deferred/then") }
  )
  assertAsyncRan()
661
})();
rossberg@chromium.org's avatar
rossberg@chromium.org committed
662 663 664 665 666 667 668 669 670

(function() {
  Promise.all([]).chain(
    function(x) { assertAsync(x.length === 0, "all/resolve/empty") },
    assertUnreachable
  )
  assertAsyncRan()
})();

671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
(function() {
  function testPromiseAllNonIterable(value) {
    Promise.all(value).chain(
        assertUnreachable,
        function(r) {
          assertAsync(r instanceof TypeError, 'all/non iterable');
        });
    assertAsyncRan();
  }
  testPromiseAllNonIterable(null);
  testPromiseAllNonIterable(undefined);
  testPromiseAllNonIterable({});
  testPromiseAllNonIterable(42);
})();

(function() {
  var deferred = Promise.defer();
  var p = deferred.promise;
  function* f() {
    yield 1;
    yield p;
    yield 3;
  }
  Promise.all(f()).chain(
      function(x) {
        assertAsync(x.length === 3, "all/resolve/iterable");
        assertAsync(x[0] === 1, "all/resolve/iterable/0");
        assertAsync(x[1] === 2, "all/resolve/iterable/1");
        assertAsync(x[2] === 3, "all/resolve/iterable/2");
      },
      assertUnreachable);
  deferred.resolve(2);
  assertAsyncRan();
  assertAsyncRan();
  assertAsyncRan();
  assertAsyncRan();
})();


rossberg@chromium.org's avatar
rossberg@chromium.org committed
710
(function() {
711
  var deferred1 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
712
  var p1 = deferred1.promise
713
  var deferred2 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
714
  var p2 = deferred2.promise
715
  var deferred3 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
  var p3 = deferred3.promise
  Promise.all([p1, p2, p3]).chain(
    function(x) {
      assertAsync(x.length === 3, "all/resolve")
      assertAsync(x[0] === 1, "all/resolve/0")
      assertAsync(x[1] === 2, "all/resolve/1")
      assertAsync(x[2] === 3, "all/resolve/2")
    },
    assertUnreachable
  )
  deferred1.resolve(1)
  deferred3.resolve(3)
  deferred2.resolve(2)
  assertAsyncRan()
  assertAsyncRan()
  assertAsyncRan()
  assertAsyncRan()
})();

(function() {
736
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
737
  var p1 = deferred.promise
738
  var p2 = Promise.accept(2)
739
  var p3 = Promise.defer().promise
rossberg@chromium.org's avatar
rossberg@chromium.org committed
740 741 742 743 744 745 746 747
  Promise.all([p1, p2, p3]).chain(
    assertUnreachable,
    assertUnreachable
  )
  deferred.resolve(1)
})();

(function() {
748
  var deferred1 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
749
  var p1 = deferred1.promise
750
  var deferred2 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
751
  var p2 = deferred2.promise
752
  var deferred3 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
753 754 755 756 757 758 759 760 761 762 763
  var p3 = deferred3.promise
  Promise.all([p1, p2, p3]).chain(
    assertUnreachable,
    function(x) { assertAsync(x === 2, "all/reject") }
  )
  deferred1.resolve(1)
  deferred3.resolve(3)
  deferred2.reject(2)
  assertAsyncRan()
})();

764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
(function() {
  'use strict';
  var getCalls = 0;
  var funcCalls = 0;
  var nextCalls = 0;
  defineProperty(numberPrototype, symbolIterator, {
    get: function() {
      assertEquals('number', typeof this);
      getCalls++;
      return function() {
        assertEquals('number', typeof this);
        funcCalls++;
        var n = this;
        var i = 0
        return {
          next() {
            nextCalls++;
            return {value: i++, done: i > n};
          }
        };
      };
    },
    configurable: true
  });

  Promise.all(3).chain(
      function(x) {
        assertAsync(x.length === 3, "all/iterable/number/length");
        assertAsync(x[0] === 0, "all/iterable/number/0");
        assertAsync(x[1] === 1, "all/iterable/number/1");
        assertAsync(x[2] === 2, "all/iterable/number/2");
      },
      assertUnreachable);
  delete numberPrototype[symbolIterator];

  assertEquals(getCalls, 1);
  assertEquals(funcCalls, 1);
  assertEquals(nextCalls, 3 + 1);  // + 1 for {done: true}
  assertAsyncRan();
  assertAsyncRan();
  assertAsyncRan();
  assertAsyncRan();
})();


rossberg@chromium.org's avatar
rossberg@chromium.org committed
809
(function() {
810
  Promise.race([]).chain(
rossberg@chromium.org's avatar
rossberg@chromium.org committed
811 812 813 814 815 816
    assertUnreachable,
    assertUnreachable
  )
})();

(function() {
817 818 819
  var p1 = Promise.accept(1)
  var p2 = Promise.accept(2)
  var p3 = Promise.accept(3)
820 821
  Promise.race([p1, p2, p3]).chain(
    function(x) { assertAsync(x === 1, "resolved/one") },
rossberg@chromium.org's avatar
rossberg@chromium.org committed
822 823 824 825 826 827
    assertUnreachable
  )
  assertAsyncRan()
})();

(function() {
828 829 830
  var p1 = Promise.accept(1)
  var p2 = Promise.accept(2)
  var p3 = Promise.accept(3)
831 832
  Promise.race([0, p1, p2, p3]).chain(
    function(x) { assertAsync(x === 0, "resolved-const/one") },
rossberg@chromium.org's avatar
rossberg@chromium.org committed
833 834 835 836 837
    assertUnreachable
  )
  assertAsyncRan()
})();

838 839
(function() {
  var deferred1 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
840
  var p1 = deferred1.promise
841
  var deferred2 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
842
  var p2 = deferred2.promise
843
  var deferred3 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
844
  var p3 = deferred3.promise
845
  Promise.race([p1, p2, p3]).chain(
rossberg@chromium.org's avatar
rossberg@chromium.org committed
846 847 848 849 850 851 852 853 854
    function(x) { assertAsync(x === 3, "one/resolve") },
    assertUnreachable
  )
  deferred3.resolve(3)
  deferred1.resolve(1)
  assertAsyncRan()
})();

(function() {
855
  var deferred = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
856
  var p1 = deferred.promise
857
  var p2 = Promise.accept(2)
858 859
  var p3 = Promise.defer().promise
  Promise.race([p1, p2, p3]).chain(
rossberg@chromium.org's avatar
rossberg@chromium.org committed
860 861 862 863 864 865 866 867
    function(x) { assertAsync(x === 2, "resolved/one") },
    assertUnreachable
  )
  deferred.resolve(1)
  assertAsyncRan()
})();

(function() {
868
  var deferred1 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
869
  var p1 = deferred1.promise
870
  var deferred2 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
871
  var p2 = deferred2.promise
872
  var deferred3 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
873
  var p3 = deferred3.promise
874
  Promise.race([p1, p2, p3]).chain(
rossberg@chromium.org's avatar
rossberg@chromium.org committed
875 876 877 878 879 880 881 882 883
    function(x) { assertAsync(x === 3, "one/resolve/reject") },
    assertUnreachable
  )
  deferred3.resolve(3)
  deferred1.reject(1)
  assertAsyncRan()
})();

(function() {
884
  var deferred1 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
885
  var p1 = deferred1.promise
886
  var deferred2 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
887
  var p2 = deferred2.promise
888
  var deferred3 = Promise.defer()
rossberg@chromium.org's avatar
rossberg@chromium.org committed
889
  var p3 = deferred3.promise
890
  Promise.race([p1, p2, p3]).chain(
rossberg@chromium.org's avatar
rossberg@chromium.org committed
891 892 893 894 895 896 897 898
    assertUnreachable,
    function(x) { assertAsync(x === 3, "one/reject/resolve") }
  )
  deferred3.reject(3)
  deferred1.resolve(1)
  assertAsyncRan()
})();

899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995

(function() {
  function testPromiseRaceNonIterable(value) {
    Promise.race(value).chain(
        assertUnreachable,
        function(r) {
          assertAsync(r instanceof TypeError, 'race/non iterable');
        });
    assertAsyncRan();
  }
  testPromiseRaceNonIterable(null);
  testPromiseRaceNonIterable(undefined);
  testPromiseRaceNonIterable({});
  testPromiseRaceNonIterable(42);
})();


(function() {
  var deferred1 = Promise.defer()
  var p1 = deferred1.promise
  var deferred2 = Promise.defer()
  var p2 = deferred2.promise
  var deferred3 = Promise.defer()
  var p3 = deferred3.promise
  function* f() {
    yield p1;
    yield p2;
    yield p3;
  }
  Promise.race(f()).chain(
    function(x) { assertAsync(x === 3, "race/iterable/resolve/reject") },
    assertUnreachable
  )
  deferred3.resolve(3)
  deferred1.reject(1)
  assertAsyncRan()
})();

(function() {
  var deferred1 = Promise.defer()
  var p1 = deferred1.promise
  var deferred2 = Promise.defer()
  var p2 = deferred2.promise
  var deferred3 = Promise.defer()
  var p3 = deferred3.promise
  function* f() {
    yield p1;
    yield p2;
    yield p3;
  }
  Promise.race(f()).chain(
    assertUnreachable,
    function(x) { assertAsync(x === 3, "race/iterable/reject/resolve") }
  )
  deferred3.reject(3)
  deferred1.resolve(1)
  assertAsyncRan()
})();

(function() {
  'use strict';
  var getCalls = 0;
  var funcCalls = 0;
  var nextCalls = 0;
  defineProperty(numberPrototype, symbolIterator, {
    get: function() {
      assertEquals('number', typeof this);
      getCalls++;
      return function() {
        assertEquals('number', typeof this);
        funcCalls++;
        var n = this;
        var i = 0
        return {
          next() {
            nextCalls++;
            return {value: i++, done: i > n};
          }
        };
      };
    },
    configurable: true
  });

  Promise.race(3).chain(
      function(x) {
        assertAsync(x === 0, "race/iterable/number");
      },
      assertUnreachable);
  delete numberPrototype[symbolIterator];

  assertEquals(getCalls, 1);
  assertEquals(funcCalls, 1);
  assertEquals(nextCalls, 3 + 1);  // + 1 for {done: true}
  assertAsyncRan();
})();

rossberg@chromium.org's avatar
rossberg@chromium.org committed
996 997 998 999
(function() {
  var log
  function MyPromise(resolver) {
    log += "n"
1000 1001 1002 1003 1004 1005 1006 1007
    var promise = new Promise(function(resolve, reject) {
      resolver(
        function(x) { log += "x" + x; resolve(x) },
        function(r) { log += "r" + r; reject(r) }
      )
    })
    promise.__proto__ = MyPromise.prototype
    return promise
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1008 1009 1010
  }

  MyPromise.__proto__ = Promise
1011
  MyPromise.defer = function() {
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1012
    log += "d"
1013
    return call(this.__proto__.defer, this)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1014 1015 1016 1017
  }

  MyPromise.prototype.__proto__ = Promise.prototype
  MyPromise.prototype.chain = function(resolve, reject) {
1018
    log += "c"
1019
    return call(this.__proto__.__proto__.chain, this, resolve, reject)
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1020 1021 1022 1023 1024
  }

  log = ""
  var p1 = new MyPromise(function(resolve, reject) { resolve(1) })
  var p2 = new MyPromise(function(resolve, reject) { reject(2) })
1025 1026 1027
  var d3 = MyPromise.defer()
  assertTrue(d3.promise instanceof Promise, "subclass/instance")
  assertTrue(d3.promise instanceof MyPromise, "subclass/instance-my3")
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1028 1029 1030
  assertTrue(log === "nx1nr2dn", "subclass/create")

  log = ""
1031 1032
  var p4 = MyPromise.resolve(4)
  var p5 = MyPromise.reject(5)
1033
  assertTrue(p4 instanceof MyPromise, "subclass/instance4")
1034
  assertTrue(p4 instanceof MyPromise, "subclass/instance-my4")
1035
  assertTrue(p5 instanceof MyPromise, "subclass/instance5")
1036
  assertTrue(p5 instanceof MyPromise, "subclass/instance-my5")
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1037 1038 1039 1040
  d3.resolve(3)
  assertTrue(log === "nx4nr5x3", "subclass/resolve")

  log = ""
1041 1042 1043 1044
  var d6 = MyPromise.defer()
  d6.promise.chain(function(x) {
    return new Promise(function(resolve) { resolve(x) })
  }).chain(function() {})
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1045
  d6.resolve(6)
1046
  assertTrue(log === "dncncnx6", "subclass/chain")
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1047 1048

  log = ""
1049
  Promise.all([11, Promise.accept(12), 13, MyPromise.accept(14), 15, 16])
1050 1051

  assertTrue(log === "nx14", "subclass/all/arg")
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1052 1053

  log = ""
1054
  MyPromise.all([21, Promise.accept(22), 23, MyPromise.accept(24), 25, 26])
1055 1056
  assertTrue(log === "nx24nnx21nnx[object Promise]nnx23nnnx25nnx26n",
             "subclass/all/self")
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1057 1058
})();

1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
(function() {
  'use strict';

  class Pact extends Promise { }
  class Vow  extends Pact    { }
  class Oath extends Vow     { }

  Oath.constructor = Vow;

  assertTrue(Pact.resolve(Pact.resolve()).constructor === Pact,
             "subclass/resolve/own");

  assertTrue(Pact.resolve(Promise.resolve()).constructor === Pact,
             "subclass/resolve/ancestor");

  assertTrue(Pact.resolve(Vow.resolve()).constructor === Pact,
             "subclass/resolve/descendant"); var vow = Vow.resolve();

  vow.constructor = Oath;
  assertTrue(Oath.resolve(vow) === vow,
             "subclass/resolve/descendant with transplanted own constructor");
}());
rossberg@chromium.org's avatar
rossberg@chromium.org committed
1081

1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
(function() {
  var thenCalled = false;

  var resolve;
  var promise = new Promise(function(res) { resolve = res; });
  resolve({ then() { thenCalled = true; throw new Error(); } });
  assertLater(function() { return thenCalled; }, "resolve-with-thenable");
});

(function() {
  var calledWith;

  var resolve;
  var p1 = (new Promise(function(res) { resolve = res; }));
  var p2 = p1.then(function(v) {
    return {
      then(resolve, reject) { resolve({ then() { calledWith = v }}); }
    };
  });

  resolve({ then(resolve) { resolve(2); } });
  assertLater(function() { return calledWith === 2; },
              "resolve-with-thenable2");
})();

(function() {
  var p = Promise.resolve();
  var callCount = 0;
  defineProperty(p, "constructor", {
    get: function() { ++callCount; return Promise; }
  });
  p.then();
  assertEquals(1, callCount);
})();

rossberg@chromium.org's avatar
rossberg@chromium.org committed
1117
assertAsyncDone()