UNPKG

next-task

Version:

Implementation of nextTick (microtask queue) for all platforms (like asap.js).

457 lines (335 loc) 10.1 kB
'use strict'; /* global describe, it */ describe('nextTask', function() { var scaffold = require("./scaffold-for-tests"); var nextTask = require("../src/next-task"); var syncFlush = 0; var global = Function('return this')(), window = global.window, document = global.document; /** Use global nextTask for browser tests with different "use". */ if (global.nextTask) nextTask = global.nextTask; var isNode = ({}).toString.call(global.process) === '[object process]'; var useText = 'Use ' + nextTask.use; if (isNode) { console.log(useText); } else if (document && document.body) { var p = document.createElement('p'); p.style = 'font-size: 48px; margin: 48px;'; p.innerHTML = useText; document.body.insertBefore(p, document.body.firstChild); } /** * Throw error, if value in not true. * @param {*} value * @param {string} msg * @throws {Error} */ function assert(value, msg) { if (value !== true) throw Error('Assert ' + (msg || '')); } describe('sync', function() { it('exists', function() { assert(typeof nextTask === 'function'); }); it('call without error for false values', function() { nextTask(); nextTask(undefined); nextTask(null); nextTask(0); nextTask(''); }); it('doesnt throw with function as argument', function() { nextTask(function() {}); nextTask(function() {return null;}); }); it('doesnt throw with object with call-method', function() { nextTask({call: Object}); nextTask(Function); }); if (!isNode) it('doesnt throw even task throw error (but need manual flush)', function() { nextTask(function() {throw Error();}); syncFlush++; nextTask(function() {throw Error();}); syncFlush++; nextTask(function() {throw Error();}); syncFlush++; nextTask(function() {throw Error();}); syncFlush++; } ); it('has use property', function() { assert(typeof nextTask.use === 'string'); }); it('has setCapacity method', function() { assert(typeof nextTask.setCapacity === 'function'); }); it('setCapacity dont throw with any arguments', function() { var capacity = nextTask.setCapacity(); nextTask.setCapacity(); nextTask.setCapacity(undefined); nextTask.setCapacity(null); nextTask.setCapacity(0); nextTask.setCapacity(-0); nextTask.setCapacity(-5); nextTask.setCapacity(Infinity); nextTask.setCapacity(capacity); }); it('return new capacity', function() { var capacity = nextTask.setCapacity(); assert(Number(capacity) === capacity); assert(nextTask.setCapacity() === capacity); assert(nextTask.setCapacity(0) === capacity); assert(nextTask.setCapacity('') === capacity); assert(nextTask.setCapacity(null) === capacity); assert(nextTask.setCapacity(undefined) === capacity); assert(nextTask.setCapacity({}) === capacity); assert(nextTask.setCapacity([]) === capacity); assert(nextTask.setCapacity(2048) === 2048); assert(nextTask.setCapacity(capacity) === capacity); }); }); describe('async', function() { it('should flush', function(done) { var MS_WAIT = 256; this.timeout(MS_WAIT * (syncFlush + 2)); function onerror(error) { if (global.console) { console.log('Global error: ' + error); } } /* Switch off Mocha asunc error cather. */ if (window) window.onerror = onerror; for (var i = 0; i < syncFlush; ++i) { setTimeout(nextTask, MS_WAIT*(i + 1)); } nextTask(done); }); it('should do tasks', function(done) { nextTask(done); }); it('should do tasks quickly', function(done) { this.timeout(100); nextTask(done); }); it('should do task once', function(done) { var called = 0; nextTask(function() { called++; }); nextTask(function() { if (called === 1) done(); }); }); it('should do all tasks', function(done) { var called = 0; nextTask(function() { called++; }); nextTask(function() { called++; }); nextTask(function() { called++; }); nextTask(function() { if (called === 3) done(); }); }); it('should do all tasks in correct order', function(done) { var called = 0; nextTask(function() { if (called === 0) called++; }); nextTask(function() { if (called === 1) called++; }); nextTask(function() { if (called === 2) called++; }); nextTask(function() { if (called === 3) done(); }); }); it('should do thousands tasks in correct order', function(done) { var called = 0, index = 0, REPEATS = 4096; function getNext() { var fnumber = index++; return function() { assert( fnumber === called, 'fnumber: ' + fnumber + ', called: ' + called ); called++; }; } for(var i = 0; i < REPEATS; ++i) { nextTask(getNext()); } nextTask(function() { if (called === REPEATS) done(); }); }); it('should do thousands nested tasks', function(done) { var index = 0, REPEATS = 8192; this.timeout(32*REPEATS); function next() { if (index++ === REPEATS) return done(); nextTask(next); } nextTask(next); }); }); describe('original rawAsap tests', function() { var expect = scaffold.expect; var asap = nextTask; var MAX_RECURSION = 10; var WAIT_FOR_NORMAL_CASE = 100; it("calls task in a future turn", function (done) { var called = false; asap(function () { called = true; done(); }); expect(called).toBe(false); }); it("calls task.call method in a future turn", function (done) { var called = false; asap({call: function () { called = true; done(); }}); expect(called).toBe(false); }); it("calls multiple tasks in order", function (done) { var calls = []; asap(function () { calls.push(0); }); asap(function () { calls.push(1); }); asap(function () { calls.push(2); }); expect(calls).toEqual([]); setTimeout(function () { expect(calls).toEqual([0, 1, 2]); done(); }, WAIT_FOR_NORMAL_CASE); }); it("calls tasks in breadth-first order", function (done) { var calls = []; asap(function () { calls.push(0); asap(function () { calls.push(2); asap(function () { calls.push(5); }); asap(function () { calls.push(6); }); }); asap(function () { calls.push(3); }); }); asap(function () { calls.push(1); asap(function () { calls.push(4); }); }); expect(calls).toEqual([]); setTimeout(function () { expect(calls).toEqual([0, 1, 2, 3, 4, 5, 6]); done(); }, WAIT_FOR_NORMAL_CASE); }); it("can schedule more than capacity tasks", function(done) { var target = 1060; var targetList = []; var i; for (i=0; i<target; i++) { targetList.push(i); } var newList = []; for (i=0; i<target; i++) { (function(i) { asap(function() { newList.push(i); }); })(i); } setTimeout(function () { expect(newList).toEqual(targetList); done(); }, WAIT_FOR_NORMAL_CASE); }); it("can schedule more than capacity*2 tasks", function(done) { var target = 2060; var targetList = []; var i; for (i=0; i<target; i++) { targetList.push(i); } var newList = []; for (i=0; i<target; i++) { (function(i) { asap(function() { newList.push(i); }); })(i); } setTimeout(function () { expect(newList).toEqual(targetList); done(); }, WAIT_FOR_NORMAL_CASE); }); // Recursion it("can schedule tasks recursively", function (done) { var steps = []; asap(function () { steps.push(0); asap(function () { steps.push(2); asap(function () { steps.push(4); }); steps.push(3); }); steps.push(1); }); setTimeout(function () { expect(steps).toEqual([0, 1, 2, 3, 4]); done(); }, WAIT_FOR_NORMAL_CASE); }); it("can recurse " + MAX_RECURSION + " tasks deep", function (done) { var timesRecursed = 0; function go() { if (++timesRecursed < MAX_RECURSION) { asap(go); } } asap(go); setTimeout(function () { expect(timesRecursed).toBe(MAX_RECURSION); done(); }, WAIT_FOR_NORMAL_CASE); }); it("can execute two branches of recursion in parallel", function (done) { var timesRecursed1 = 0; var timesRecursed2 = 0; var calls = []; function go1() { calls.push(timesRecursed1 * 2); if (++timesRecursed1 < MAX_RECURSION) { asap(go1); } } function go2() { calls.push(timesRecursed2 * 2 + 1); if (++timesRecursed2 < MAX_RECURSION) { asap(go2); } } asap(go1); asap(go2); setTimeout(function () { expect(calls.length).toBe(MAX_RECURSION * 2); for (var index = 0; index < MAX_RECURSION * 2; index++) { expect(calls[index]).toBe(index); } done(); }, WAIT_FOR_NORMAL_CASE); }); }); });