diff --git a/src/jsonata.js b/src/jsonata.js index c54b0ca8..aeaf8fbe 100644 --- a/src/jsonata.js +++ b/src/jsonata.js @@ -2137,12 +2137,11 @@ var jsonata = (function() { } var environment = createFrame(staticFrame); - var timestamp = new Date(); // will be overridden on each call to evalute() environment.bind('now', defineFunction(function(picture, timezone) { - return datetime.fromMillis(timestamp.getTime(), picture, timezone); + return datetime.fromMillis(this.environment.timestamp.getTime(), picture, timezone); }, '')); environment.bind('millis', defineFunction(function() { - return timestamp.getTime(); + return this.environment.timestamp.getTime(); }, '<:n>')); return { @@ -2169,8 +2168,7 @@ var jsonata = (function() { // capture the timestamp and put it in the execution environment // the $now() and $millis() functions will return this value - whenever it is called - timestamp = new Date(); - exec_env.timestamp = timestamp; + exec_env.timestamp = new Date(); exec_env.options = options; exec_env.createSequence = function() { diff --git a/test/implementation-tests.js b/test/implementation-tests.js index e213ed7d..8cf6abe8 100644 --- a/test/implementation-tests.js +++ b/test/implementation-tests.js @@ -176,6 +176,37 @@ describe("Functions with side-effects", () => { }); }); + describe("$millis() and $now() are not affected by an overlapping evaluate() call", function() { + it("should return the same value within each evaluation", async function() { + var expr = jsonata("($m := $millis(); $n := $now(); $pause($m); [$m = $millis(), $n = $now()])"); + var paused, release; + var reached = new Promise((resolve) => { paused = resolve; }); + var resume = new Promise((resolve) => { release = resolve; }); + // the first evaluation suspends part way through, until released + var first = expr.evaluate(testdata2, { + pause: function(millis) { + paused(millis); + return resume; + } + }); + var firstMillis = await reached; + // make sure the overlapping evaluation captures a later timestamp + while (Date.now() <= firstMillis) { + await new Promise((resolve) => setTimeout(resolve, 1)); + } + var secondMillis; + var second = await expr.evaluate(testdata2, { + pause: function(millis) { + secondMillis = millis; + } + }); + release(); + expect(secondMillis).to.be.above(firstMillis); + expect(second).to.deep.equal([true, true]); + expect(await first).to.deep.equal([true, true]); + }); + }); + describe("$millis() returns milliseconds since the epoch", function() { it("should return result object", function() { var expr = jsonata("$millis()");