805 lines
21 KiB
JavaScript
805 lines
21 KiB
JavaScript
/**
|
|
* Copyright (c) 2014, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* https://raw.github.com/facebook/regenerator/master/LICENSE file. An
|
|
* additional grant of patent rights can be found in the PATENTS file in
|
|
* the same directory.
|
|
*/
|
|
|
|
var assert = require("assert");
|
|
|
|
describe("async functions and await expressions", function () {
|
|
Promise = require("promise");
|
|
|
|
describe("regeneratorRuntime", function () {
|
|
it("should be defined globally", function () {
|
|
var global = Function("return this")();
|
|
assert.ok("regeneratorRuntime" in global);
|
|
assert.strictEqual(global.regeneratorRuntime, regeneratorRuntime);
|
|
});
|
|
|
|
it("should have a .wrap method", function () {
|
|
assert.strictEqual(typeof regeneratorRuntime.wrap, "function");
|
|
});
|
|
});
|
|
|
|
describe("Promise", function () {
|
|
it("should be defined globally", function () {
|
|
var global = Function("return this")();
|
|
assert.ok("Promise" in global);
|
|
assert.strictEqual(global.Promise, Promise);
|
|
});
|
|
|
|
it("should be a function", function () {
|
|
assert.strictEqual(typeof Promise, "function");
|
|
});
|
|
});
|
|
|
|
describe("no-await async function", function () {
|
|
it("should return a Promise", function (done) {
|
|
var called = false;
|
|
|
|
function noAwait(value) {
|
|
return regeneratorRuntime.async(function noAwait$(_context) {
|
|
while (1) switch (_context.prev = _context.next) {
|
|
case 0:
|
|
called = true;
|
|
return _context.abrupt("return", value);
|
|
|
|
case 2:
|
|
case "end":
|
|
return _context.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
var promise = noAwait("asdf");
|
|
assert.strictEqual(called, true);
|
|
|
|
promise.then(function (value) {
|
|
assert.strictEqual(called, true);
|
|
assert.strictEqual(value, "asdf");
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
});
|
|
|
|
describe("one-await async function", function () {
|
|
it("should finish asynchronously", function (done) {
|
|
var flag1 = false;
|
|
var flag2 = false;
|
|
|
|
function oneAwait(value) {
|
|
var result;
|
|
return regeneratorRuntime.async(function oneAwait$(_context2) {
|
|
while (1) switch (_context2.prev = _context2.next) {
|
|
case 0:
|
|
flag1 = true;
|
|
_context2.next = 3;
|
|
return regeneratorRuntime.awrap(value);
|
|
|
|
case 3:
|
|
result = _context2.sent;
|
|
|
|
flag2 = true;
|
|
return _context2.abrupt("return", result);
|
|
|
|
case 6:
|
|
case "end":
|
|
return _context2.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
var promise = oneAwait("asdf");
|
|
assert.strictEqual(flag1, true);
|
|
assert.strictEqual(flag2, false);
|
|
|
|
promise.then(function (value) {
|
|
assert.strictEqual(flag2, true);
|
|
assert.strictEqual(value, "asdf");
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
});
|
|
|
|
describe("nested async function calls", function () {
|
|
it("should evaluate in the right order", function (done) {
|
|
var markers = [];
|
|
|
|
function innerMost(marker) {
|
|
return regeneratorRuntime.async(function innerMost$(_context3) {
|
|
while (1) switch (_context3.prev = _context3.next) {
|
|
case 0:
|
|
markers.push(marker);
|
|
_context3.next = 3;
|
|
return regeneratorRuntime.awrap(marker);
|
|
|
|
case 3:
|
|
return _context3.abrupt("return", _context3.sent);
|
|
|
|
case 4:
|
|
case "end":
|
|
return _context3.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
function inner(marker) {
|
|
return regeneratorRuntime.async(function inner$(_context4) {
|
|
while (1) switch (_context4.prev = _context4.next) {
|
|
case 0:
|
|
markers.push(marker);
|
|
|
|
_context4.t0 = assert;
|
|
_context4.next = 4;
|
|
return regeneratorRuntime.awrap(innerMost(marker + 1));
|
|
|
|
case 4:
|
|
_context4.t1 = _context4.sent;
|
|
_context4.t2 = marker + 1;
|
|
|
|
_context4.t0.strictEqual.call(_context4.t0, _context4.t1, _context4.t2);
|
|
|
|
markers.push(marker + 2);
|
|
|
|
_context4.t3 = assert;
|
|
_context4.next = 11;
|
|
return regeneratorRuntime.awrap(innerMost(marker + 3));
|
|
|
|
case 11:
|
|
_context4.t4 = _context4.sent;
|
|
_context4.t5 = marker + 3;
|
|
|
|
_context4.t3.strictEqual.call(_context4.t3, _context4.t4, _context4.t5);
|
|
|
|
markers.push(marker + 4);
|
|
|
|
case 15:
|
|
case "end":
|
|
return _context4.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
function outer() {
|
|
return regeneratorRuntime.async(function outer$(_context5) {
|
|
while (1) switch (_context5.prev = _context5.next) {
|
|
case 0:
|
|
markers.push(0);
|
|
_context5.next = 3;
|
|
return regeneratorRuntime.awrap(inner(1));
|
|
|
|
case 3:
|
|
markers.push(6);
|
|
_context5.next = 6;
|
|
return regeneratorRuntime.awrap(inner(7));
|
|
|
|
case 6:
|
|
markers.push(12);
|
|
|
|
case 7:
|
|
case "end":
|
|
return _context5.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
outer().then(function () {
|
|
var expected = [];
|
|
for (var i = 0; i <= 12; ++i) expected.push(i);
|
|
assert.deepEqual(markers, expected);
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
});
|
|
|
|
describe("dependent promises", function () {
|
|
it("should be awaitable out of order", function (done) {
|
|
function outer(value) {
|
|
var resolved, p1, v2, v1;
|
|
return regeneratorRuntime.async(function outer$(_context6) {
|
|
while (1) switch (_context6.prev = _context6.next) {
|
|
case 0:
|
|
resolved = false;
|
|
p1 = new Promise(function (resolve) {
|
|
setTimeout(function () {
|
|
resolve(value + 1);
|
|
resolved = true;
|
|
}, 0);
|
|
});
|
|
|
|
assert.strictEqual(resolved, false);
|
|
|
|
_context6.next = 5;
|
|
return regeneratorRuntime.awrap(p1.then(function (value) {
|
|
return value + 1;
|
|
}));
|
|
|
|
case 5:
|
|
v2 = _context6.sent;
|
|
|
|
assert.strictEqual(resolved, true);
|
|
|
|
_context6.next = 9;
|
|
return regeneratorRuntime.awrap(p1);
|
|
|
|
case 9:
|
|
v1 = _context6.sent;
|
|
return _context6.abrupt("return", [v1, v2]);
|
|
|
|
case 11:
|
|
case "end":
|
|
return _context6.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
outer(1).then(function (pair) {
|
|
assert.deepEqual(pair, [2, 3]);
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
});
|
|
|
|
describe("rejected promises", function () {
|
|
it("should cause await expressions to throw", function (done) {
|
|
var error = new Error("rejected");
|
|
|
|
function f(arg) {
|
|
return regeneratorRuntime.async(function f$(_context7) {
|
|
while (1) switch (_context7.prev = _context7.next) {
|
|
case 0:
|
|
_context7.prev = 0;
|
|
_context7.next = 3;
|
|
return regeneratorRuntime.awrap(arg);
|
|
|
|
case 3:
|
|
return _context7.abrupt("return", _context7.sent);
|
|
|
|
case 6:
|
|
_context7.prev = 6;
|
|
_context7.t0 = _context7["catch"](0);
|
|
|
|
assert.strictEqual(_context7.t0, error);
|
|
return _context7.abrupt("return", "did throw");
|
|
|
|
case 10:
|
|
case "end":
|
|
return _context7.stop();
|
|
}
|
|
}, null, this, [[0, 6]]);
|
|
}
|
|
|
|
Promise.all([f(Promise.reject(error)), f(Promise.resolve("did not throw"))]).then(function (results) {
|
|
assert.deepEqual(results, ["did throw", "did not throw"]);
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
|
|
it("should be returned by exceptional async functions", function (done) {
|
|
var error = new Error("rejected");
|
|
|
|
function e(arg) {
|
|
return regeneratorRuntime.async(function e$(_context8) {
|
|
while (1) switch (_context8.prev = _context8.next) {
|
|
case 0:
|
|
if (!arg) {
|
|
_context8.next = 2;
|
|
break;
|
|
}
|
|
|
|
throw arg;
|
|
|
|
case 2:
|
|
return _context8.abrupt("return", "did not throw");
|
|
|
|
case 3:
|
|
case "end":
|
|
return _context8.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
function f(arg) {
|
|
return regeneratorRuntime.async(function f$(_context9) {
|
|
while (1) switch (_context9.prev = _context9.next) {
|
|
case 0:
|
|
_context9.next = 2;
|
|
return regeneratorRuntime.awrap(e(arg));
|
|
|
|
case 2:
|
|
return _context9.abrupt("return", _context9.sent);
|
|
|
|
case 3:
|
|
case "end":
|
|
return _context9.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
function g(arg) {
|
|
return regeneratorRuntime.async(function g$(_context10) {
|
|
while (1) switch (_context10.prev = _context10.next) {
|
|
case 0:
|
|
_context10.next = 2;
|
|
return regeneratorRuntime.awrap(f(arg));
|
|
|
|
case 2:
|
|
return _context10.abrupt("return", _context10.sent);
|
|
|
|
case 3:
|
|
case "end":
|
|
return _context10.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
function h(arg) {
|
|
return regeneratorRuntime.async(function h$(_context11) {
|
|
while (1) switch (_context11.prev = _context11.next) {
|
|
case 0:
|
|
_context11.next = 2;
|
|
return regeneratorRuntime.awrap(Promise.all([g(arg), Promise.resolve("dummy")]));
|
|
|
|
case 2:
|
|
return _context11.abrupt("return", _context11.sent);
|
|
|
|
case 3:
|
|
case "end":
|
|
return _context11.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
Promise.all([h(error).then(function () {
|
|
done(new Error("should not have resolved"));
|
|
}, function (e) {
|
|
assert.strictEqual(e, error);
|
|
return "ok1";
|
|
}), h(null).then(function (result) {
|
|
assert.deepEqual(result, ["did not throw", "dummy"]);
|
|
return "ok2";
|
|
})]).then(function (results) {
|
|
assert.deepEqual(results, ["ok1", "ok2"]);
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
|
|
it("should propagate failure when returned", function () {
|
|
var rejection = new Error("rejection");
|
|
|
|
function f() {
|
|
return regeneratorRuntime.async(function f$(_context12) {
|
|
while (1) switch (_context12.prev = _context12.next) {
|
|
case 0:
|
|
return _context12.abrupt("return", new Promise(function (resolve, reject) {
|
|
reject(rejection);
|
|
}));
|
|
|
|
case 1:
|
|
case "end":
|
|
return _context12.stop();
|
|
}
|
|
}, null, this);
|
|
}
|
|
|
|
return f().then(function (result) {
|
|
assert.ok(false, "should have been rejected");
|
|
}, function (error) {
|
|
assert.strictEqual(error, rejection);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("async function expressions", function () {
|
|
it("should be allowed", function (done) {
|
|
(function _callee(arg) {
|
|
return regeneratorRuntime.async(function _callee$(_context13) {
|
|
while (1) switch (_context13.prev = _context13.next) {
|
|
case 0:
|
|
_context13.next = 2;
|
|
return regeneratorRuntime.awrap(arg);
|
|
|
|
case 2:
|
|
return _context13.abrupt("return", _context13.sent);
|
|
|
|
case 3:
|
|
case "end":
|
|
return _context13.stop();
|
|
}
|
|
}, null, this);
|
|
})(Promise.resolve(1234)).then(function (value) {
|
|
assert.strictEqual(value, 1234);
|
|
done();
|
|
}).catch(done);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("async generator functions", function () {
|
|
it("should return a working AsyncIterator", function () {
|
|
var _marked = [gen].map(regeneratorRuntime.mark);
|
|
|
|
var markers = [];
|
|
|
|
function gen(arg) {
|
|
var sent, result;
|
|
return regeneratorRuntime.async(function gen$(_context14) {
|
|
while (1) switch (_context14.prev = _context14.next) {
|
|
case 0:
|
|
markers.push(0);
|
|
_context14.next = 3;
|
|
return arg;
|
|
|
|
case 3:
|
|
sent = _context14.sent;
|
|
|
|
markers.push(1);
|
|
_context14.next = 7;
|
|
return regeneratorRuntime.awrap(sent);
|
|
|
|
case 7:
|
|
result = _context14.sent;
|
|
|
|
markers.push(2);
|
|
_context14.t0 = assert;
|
|
_context14.t1 = regeneratorRuntime;
|
|
_context14.next = 13;
|
|
return "second";
|
|
|
|
case 13:
|
|
_context14.t2 = _context14.sent;
|
|
_context14.next = 16;
|
|
return _context14.t1.awrap.call(_context14.t1, _context14.t2);
|
|
|
|
case 16:
|
|
_context14.t3 = _context14.sent;
|
|
|
|
_context14.t0.strictEqual.call(_context14.t0, _context14.t3, "sent after second");
|
|
|
|
markers.push(3);
|
|
return _context14.abrupt("return", result);
|
|
|
|
case 20:
|
|
case "end":
|
|
return _context14.stop();
|
|
}
|
|
}, _marked[0], this);
|
|
}
|
|
|
|
var iter = gen("initial argument");
|
|
assert.deepEqual(markers, []);
|
|
|
|
var firstPromise = iter.next();
|
|
assert.deepEqual(markers, [0]);
|
|
|
|
return firstPromise.then(function (firstResult) {
|
|
assert.deepEqual(firstResult, {
|
|
value: "initial argument",
|
|
done: false
|
|
});
|
|
|
|
assert.deepEqual(markers, [0]);
|
|
|
|
return iter.next(new Promise(function (resolve) {
|
|
setTimeout(resolve, 100);
|
|
}).then(function () {
|
|
assert.deepEqual(markers, [0, 1]);
|
|
return "will become final result";
|
|
}));
|
|
}).then(function (secondResult) {
|
|
assert.deepEqual(secondResult, {
|
|
value: "second",
|
|
done: false
|
|
});
|
|
|
|
assert.deepEqual(markers, [0, 1, 2]);
|
|
|
|
return iter.next("sent after second");
|
|
}).then(function (finalResult) {
|
|
assert.deepEqual(markers, [0, 1, 2, 3]);
|
|
assert.deepEqual(finalResult, {
|
|
value: "will become final result",
|
|
done: true
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should keep results in order", function () {
|
|
var _marked2 = [range].map(regeneratorRuntime.mark);
|
|
|
|
function range(limit) {
|
|
var before, after, i;
|
|
return regeneratorRuntime.async(function range$(_context15) {
|
|
while (1) switch (_context15.prev = _context15.next) {
|
|
case 0:
|
|
before = [];
|
|
after = [];
|
|
i = 0;
|
|
|
|
case 3:
|
|
if (!(i < limit)) {
|
|
_context15.next = 11;
|
|
break;
|
|
}
|
|
|
|
before.push(i);
|
|
_context15.next = 7;
|
|
return i;
|
|
|
|
case 7:
|
|
after.push(i);
|
|
|
|
case 8:
|
|
++i;
|
|
_context15.next = 3;
|
|
break;
|
|
|
|
case 11:
|
|
assert.deepEqual(before, after);
|
|
return _context15.abrupt("return", before);
|
|
|
|
case 13:
|
|
case "end":
|
|
return _context15.stop();
|
|
}
|
|
}, _marked2[0], this);
|
|
}
|
|
|
|
var limit = 10;
|
|
var iter = range(limit);
|
|
var promises = [];
|
|
var results = [];
|
|
|
|
for (var i = 0; i < limit; ++i) {
|
|
var promise = iter.next();
|
|
promises.push(promise);
|
|
|
|
promise.then(function (result) {
|
|
assert.strictEqual(result.done, false);
|
|
results.push(result);
|
|
});
|
|
}
|
|
|
|
assert.deepEqual(results, []);
|
|
|
|
return Promise.all(promises).then(function (promiseResults) {
|
|
assert.deepEqual(results, promiseResults);
|
|
|
|
return iter.next();
|
|
}).then(function (finalResult) {
|
|
assert.deepEqual(results.map(function (result) {
|
|
return result.value;
|
|
}), finalResult.value);
|
|
|
|
assert.strictEqual(finalResult.done, true);
|
|
});
|
|
});
|
|
|
|
it("should be able to handle many awaits", function () {
|
|
var _marked3 = [gen].map(regeneratorRuntime.mark);
|
|
|
|
var awaitCount = 0;
|
|
|
|
function countAwait(i) {
|
|
return Promise.resolve(i).then(function () {
|
|
++awaitCount;
|
|
});
|
|
}
|
|
|
|
function gen(limit) {
|
|
var i;
|
|
return regeneratorRuntime.async(function gen$(_context16) {
|
|
while (1) switch (_context16.prev = _context16.next) {
|
|
case 0:
|
|
_context16.next = 2;
|
|
return regeneratorRuntime.awrap(countAwait(0));
|
|
|
|
case 2:
|
|
_context16.next = 4;
|
|
return 1;
|
|
|
|
case 4:
|
|
_context16.next = 6;
|
|
return regeneratorRuntime.awrap(countAwait(2));
|
|
|
|
case 6:
|
|
_context16.next = 8;
|
|
return regeneratorRuntime.awrap(countAwait(3));
|
|
|
|
case 8:
|
|
_context16.next = 10;
|
|
return 4;
|
|
|
|
case 10:
|
|
_context16.next = 12;
|
|
return regeneratorRuntime.awrap(countAwait(5));
|
|
|
|
case 12:
|
|
_context16.next = 14;
|
|
return regeneratorRuntime.awrap(countAwait(6));
|
|
|
|
case 14:
|
|
_context16.next = 16;
|
|
return regeneratorRuntime.awrap(countAwait(7));
|
|
|
|
case 16:
|
|
_context16.next = 18;
|
|
return 8;
|
|
|
|
case 18:
|
|
i = 0;
|
|
|
|
case 19:
|
|
if (!(i < limit)) {
|
|
_context16.next = 25;
|
|
break;
|
|
}
|
|
|
|
_context16.next = 22;
|
|
return regeneratorRuntime.awrap(countAwait(i));
|
|
|
|
case 22:
|
|
++i;
|
|
_context16.next = 19;
|
|
break;
|
|
|
|
case 25:
|
|
return _context16.abrupt("return", "done");
|
|
|
|
case 26:
|
|
case "end":
|
|
return _context16.stop();
|
|
}
|
|
}, _marked3[0], this);
|
|
}
|
|
|
|
var iter = gen(100);
|
|
|
|
return iter.next().then(function (result) {
|
|
assert.strictEqual(awaitCount, 1);
|
|
|
|
assert.deepEqual(result, {
|
|
value: 1,
|
|
done: false
|
|
});
|
|
|
|
return iter.next();
|
|
}).then(function (result) {
|
|
assert.strictEqual(awaitCount, 3);
|
|
|
|
assert.deepEqual(result, {
|
|
value: 4,
|
|
done: false
|
|
});
|
|
|
|
return iter.next();
|
|
}).then(function (result) {
|
|
assert.strictEqual(awaitCount, 6);
|
|
|
|
assert.deepEqual(result, {
|
|
value: 8,
|
|
done: false
|
|
});
|
|
|
|
return iter.next();
|
|
}).then(function (result) {
|
|
assert.strictEqual(awaitCount, 6 + 100);
|
|
|
|
assert.deepEqual(result, {
|
|
value: "done",
|
|
done: true
|
|
});
|
|
|
|
return iter.next();
|
|
}).then(function (result) {
|
|
assert.deepEqual(result, {
|
|
value: void 0,
|
|
done: true
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should not propagate exceptions between iterations", function () {
|
|
var _marked4 = [gen].map(regeneratorRuntime.mark);
|
|
|
|
function gen() {
|
|
return regeneratorRuntime.async(function gen$(_context17) {
|
|
while (1) switch (_context17.prev = _context17.next) {
|
|
case 0:
|
|
_context17.next = 2;
|
|
return 1;
|
|
|
|
case 2:
|
|
_context17.next = 4;
|
|
return 2;
|
|
|
|
case 4:
|
|
case "end":
|
|
return _context17.stop();
|
|
}
|
|
}, _marked4[0], this);
|
|
}
|
|
|
|
var iter = gen();
|
|
|
|
return iter.next().then(function (result) {
|
|
assert.deepEqual(result, {
|
|
value: 1,
|
|
done: false
|
|
});
|
|
|
|
return iter.throw(new Error("thrown from first yield"));
|
|
}).then(function () {
|
|
throw new Error("should have thrown");
|
|
}, function (error) {
|
|
assert.strictEqual(error.message, "thrown from first yield");
|
|
return iter.next();
|
|
}).then(function (result) {
|
|
assert.deepEqual(result, {
|
|
value: void 0,
|
|
done: true
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should allow yielding a rejected Promise", function () {
|
|
var _marked5 = [gen].map(regeneratorRuntime.mark);
|
|
|
|
var yielded = new Error("yielded rejection");
|
|
var returned = new Error("returned rejection");
|
|
|
|
function gen() {
|
|
return regeneratorRuntime.async(function gen$(_context18) {
|
|
while (1) switch (_context18.prev = _context18.next) {
|
|
case 0:
|
|
_context18.t0 = assert;
|
|
_context18.next = 3;
|
|
return Promise.reject(yielded);
|
|
|
|
case 3:
|
|
_context18.t1 = _context18.sent;
|
|
|
|
_context18.t0.strictEqual.call(_context18.t0, _context18.t1, "first sent");
|
|
|
|
_context18.t2 = assert;
|
|
_context18.next = 8;
|
|
return "middle";
|
|
|
|
case 8:
|
|
_context18.t3 = _context18.sent;
|
|
|
|
_context18.t2.strictEqual.call(_context18.t2, _context18.t3, "second sent");
|
|
|
|
return _context18.abrupt("return", Promise.reject(returned));
|
|
|
|
case 11:
|
|
case "end":
|
|
return _context18.stop();
|
|
}
|
|
}, _marked5[0], this);
|
|
}
|
|
|
|
var iter = gen();
|
|
|
|
return iter.next().then(function (result) {
|
|
assert.ok(false, "should have yielded a rejected Promise");
|
|
}, function (error) {
|
|
assert.strictEqual(error, yielded);
|
|
return iter.next("first sent");
|
|
}).then(function (result) {
|
|
assert.deepEqual(result, {
|
|
value: "middle",
|
|
done: false
|
|
});
|
|
return iter.next("second sent");
|
|
}).then(function (result) {
|
|
assert.ok(false, "should have returned a rejected Promise");
|
|
}, function (error) {
|
|
assert.strictEqual(error, returned);
|
|
});
|
|
});
|
|
}); |