GithubHelp home page GithubHelp logo

aurelia / task-queue Goto Github PK

View Code? Open in Web Editor NEW
36.0 9.0 21.0 1.25 MB

A simple task queue for the browser that enables the queuing of both standard tasks and micro tasks.

License: MIT License

JavaScript 100.00%

task-queue's Introduction

aurelia-task-queue

npm Version ZenHub Join the chat at https://gitter.im/aurelia/discuss CircleCI

This library is part of the Aurelia platform and contains a simple task queue for the browser that enables the queuing of both standard tasks and micro tasks.

To keep up to date on Aurelia, please visit and subscribe to the official blog and our email list. We also invite you to follow us on twitter. If you have questions, please join our community on Gitter or use stack overflow. Documentation can be found in our developer hub. If you would like to have deeper insight into our development process, please install the ZenHub Chrome or Firefox Extension and visit any of our repository's boards.

Platform Support

This library can be used in the browser only.

Building The Code

To build the code, follow these steps.

  1. Ensure that NodeJS is installed. This provides the platform on which the build tooling runs.
  2. From the project folder, execute the following command:
npm install
  1. Ensure that Gulp is installed. If you need to install it, use the following command:
npm install -g gulp
  1. To build the code, you can now run:
gulp build
  1. You will find the compiled code in the dist folder, available in three module formats: AMD, CommonJS and ES6.

  2. See gulpfile.js for other tasks related to generating the docs and linting.

Running The Tests

To run the unit tests, first ensure that you have followed the steps above in order to install all dependencies and successfully build the library. Once you have done that, proceed with these additional steps:

  1. Ensure that the Karma CLI is installed. If you need to install it, use the following command:
npm install -g karma-cli
  1. Ensure that jspm is installed. If you need to install it, use the following commnand:
npm install -g jspm
  1. Download the SystemJS module loader:
jspm dl-loader
  1. You can now run the tests with this command:
karma start

task-queue's People

Contributors

abalmush avatar cmichaelgraham avatar doktordirk avatar eisenbergeffect avatar fkleuver avatar jdanyow avatar jods4 avatar odetocode avatar plwalters avatar pndewit avatar strahilkazlachev avatar timfish avatar zewa666 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

task-queue's Issues

Flush during flush leads to not all tasks being executed

I'm submitting a bug report

  • Library Version:
    current

Please tell us about your environment:

  • Operating System:
    OSX 10.x|Linux (distro)|Windows [7|8|8.1|10]

  • Node Version:
    6.2.0

  • NPM Version:
    3.8.9
  • Browser:
    all | Chrome XX | Firefox XX | Edge XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView

  • Language:
    all | TypeScript X.X | ESNext

Current behavior:
If one of the queued micro tasks calls flushMicroTaskQueue
Queue enters the second flush cycle even though flushing is set to true
after which length of queue is set to zero
but for 1st flush call index is greater then length of queue at this point
and leads to unexpected behavior

Expected/desired behavior:

  • What is the expected behavior?
    If queue is already flushing we should ignore the second call

  • What is the motivation / use case for changing the behavior?
    prevent uncertainty

Unexpected behavior with microTask

I'm submitting a bug report

  • Library Version:
    1.1.0

Please tell us about your environment:

  • Operating System:
    Windows [10]

  • Node Version:
    5.1.0

  • NPM Version:
    3.10.8

  • JSPM OR Webpack AND Version
    CLI

  • Browser:
    Chrome 53

  • Language:
    ESNext

Current behavior:
My microTask fires, but apparently still too early in the lifecycle and I'm not getting my desired results, however, if I wrap my code into a setTimeout(()=>{},1) then I get my desired results.

This code works...

    bind() {
        if (this.appState.scrollPosition)
            this.taskQueue.queueMicroTask(() => {
                setTimeout(() => {
                    $('body').scrollTop(this.appState.scrollPosition);
                },1);
            });
    }

    canDeactivate() {
        this.appState.scrollPosition = $("body").scrollTop();
        return true;
    }

Expected/desired behavior:
This code should work, but does not...

    bind() {
        if (this.appState.scrollPosition)
            this.taskQueue.queueMicroTask(() => {
                $('body').scrollTop(this.appState.scrollPosition);
            });
    }

    canDeactivate() {
        this.appState.scrollPosition = $("body").scrollTop();
        return true;
    }
  • What is the expected behavior?

I am expecting this code to work, without the need for the setTimeout()

  • What is the motivation / use case for changing the behavior?

I thought this used to work without the setTimeout... however currently I need the setTimeout to get my desired results.

Micro optimization

Been playing around with this a little bit. 5 different approaches to requestFlush(). They all behave the same (trigger a mutation in MutationObserver in all major browsers), but the performance - however ridiculously small - is different.

1. toggle + toString (vNext)

let toggle = 1;
let node = document.createTextNode('');
...
toggle = -toggle;
node.data = toggle.toString();

2. toggle (vCurrent)

let toggle = 1;
let node = document.createTextNode('');
...
toggle = -toggle;
node.data = toggle;

3. Object lookup swap

let val = 'a';
let node = document.createTextNode('a');
let values = Object.create(null);
values['a'] = 'b';
values['b'] = 'a';
...
node.data = val = values[val];

4. Array toggle (creds to jods)

let values = ['a', 'b'];
let toggle = 0;
...
node.data = values[toggle ^= 1];

5. Coerce mutation

node.data = 0;

Benchmark: http://jsben.ch/EEXsx

Chrome
image

Edge
image

Firefox
image

Notes:

  • Chrome > FF > Edge in performance
  • Everything is slow in Edge
  • Bitwise ops, indexers and lookups are fast in Chrome
  • Indexers are slow in FF, coercion is fast in FF
  • Edge is really slow
  • Chrome is really fast
  • FF always surprises me

If it weren't for FF, I would have gone with jods' approach. I thought indexers had more overhead in Chrome too but apparently I was wrong.
I'm leaning more towards the lookup swap because = 0 just feels like it's not very spec-esque (works, but will it keep working?)

Of course, no one will ever notice the difference. Doesn't make it any less fun to experiment with these things now and then :)

Thoughts/preferences/don'ttouchit?
@bigopon @EisenbergEffect

Remarks/Questions task-queue

I was reading the task-queue source code and the following questions/remarks came to my mind. Sorry if it's irrelevant or plain wrong 😶

(1) why doesn't task-queue use setImmediate for scheduling when it can (IE)? That should be the most lightweight and efficient way when it's supported. I thought maybe you wouldn't use it at all, but in the same file, onError makes use of it.

(2) is it expected that the micro- and macro- queues race with one another?
I kind of would have expected this: tq.queueTask(x); tq.queueMicroTask(y); to result in y being executed before x but it isn't necessarily the case (in fact, the ordering is unpredictable, even when written tq.queueMicroTask(y); tq.queueTask(x)).
One can find lots of other funny cases. For instance: when processing the macro- queue, any micro-task enqueued would not be executed before all already enqueued macro-tasks have completed.

Maybe it's the way it's supposed to be, but then the doc is a bit misleading Queues a task on the micro task queue for ASAP execution..

(3) the flush methods appear as public API surface (not starting with an underscore, even present in the .d.ts definition). But they are not written in a robust way for public API. flushMicroTaskQueue() is not re-entrant and may result in duplicate code execution. flushTaskQueue() is re-entrant but may result in out of order execution.
You put the responsibility of non-reentrancy on the caller, but it's very difficult to know in arbitrary code whether you are executing inside a queue flush or not.

(4) Overall I'm wondering if we wouldn't need a priority queue here? That's what other systems have usually (see Windows/.net). Thus we could have a single structure with multiple priority (binding, rendering, idle). Seems more flexible and probably more robust to me.

Throwing error in setImmediate terminates Electron renderer process!

The following code from here causes the Electron renderer process to terminate:

setImmediate(function () {
  throw error;
});

This can easily be reproduced in Electron 1.8.6 by pasting the following into the renderer console:

window.setImmediate(() => {throw new Error('Test')})

In the main process console you will sometimes get the following:

Error: async hook stack has become corrupted (actual: 558, expected: 0)

I'm currently bodging this with the following code although this is far from ideal:

window.setImmediate = (m) => window.setTimeout(m, 0);

promised tasks

I'm submitting a feature request

  • Library Version:
    1.2.1

Please tell us about your environment:

  • Operating System:
    Linux - KDE Neon 5.12

  • Node Version:
    9.6.1

  • NPM Version:
    5.6.0

  • Yarn Version:
    1.3.2

  • JSPM OR Webpack AND Version
    webpack 4.0.1

  • Browser:
    all

  • Language:
    all

Current behavior:
TaskQueue#queueMicroTask just takes a function/Task and returns void

queueMicroTask(task: Task | Function): void;

Expected/desired behavior:
TaskQueue#queueMicroTask takes a function/Task and returns Promise<void> which resolves when the task is called.

queueMicroTask(task: Task | Function): Promise<void>;
  • What is the motivation / use case for changing the behavior?
    there are times when I need 2 separate "tasks" to happen but I need the first task's side effects (binding, valueChanged functions, etc) to settle down, before registering the second one's side effects.

currently the way to achieve this is to register the task that has side effects, and from inside that task register the second task.

some pseudocode:

@autoinject
class Page {
  constructor(private taskQueue: TaskQueue){}

  someMethod(){
    // wait for side effects to settle down
    this.taskQueue.queueMicroTask(() => {
      this.someProp = 5;

      // wait again
      this.taskQueue.queueMicroTask(() => {
        this.focusNextElementInForm();
      });
    });
  }
}

and if we wanted to have more things happen in succession, we would quickly find ourselves in callback hell. the same process written using async/await is much more readable

@autoinject
class Page {
  constructor(private taskQueue: TaskQueue){}

  async someMethod(){
    // wait for side effects to settle down
    await this.taskQueue.queueMicroTask(() => {
      this.someProp = 5;
    });
    // wait again
    await this.taskQueue.queueMicroTask(() => {
      this.focusNextElementInForm();
    });
  }
}

to achieve this behavior i'm overriding the TaskQueue#queueMicroTask method, with something along the lines of:

const oldQueueMicro = TaskQueue.prototype.queueMicroTask;
TaskQueue.prototype.queueMicroTask = function queueMicroTask(task) {
  return new Promise((resolve, reject) => {
    try {
      oldQueueMicro.call(this, () => {
        task.call();
        resolve();
      });
    } catch (e) {
      reject(e);
    }
  });
};

it works, but it's not the best

Task Queue error in unit tests running on PhantomJS

Trying to unit test a simple custom element.

export class BrainDeadSimple {
  msg = 'hello';
}

with test:

import {BrainDeadSimple} from 'src/resources/elements/brain-dead-          simple/brain-dead-simple';
import {BindingEngine} from 'aurelia-binding';
import {Container} from 'aurelia-dependency-injection';
import {TemplatingEngine} from 'aurelia-templating';
import {TaskQueue} from 'aurelia-task-queue';

describe('BrainDeadSimple', () => {

  let testee;
  let templatingEngine;
  let container;     

  beforeEach(() => {
    container = new Container();
    templatingEngine = container.get(TemplatingEngine);        
    testee = templatingEngine.createViewModelForUnitTest(BrainDeadSimple);
  });

  it('should set msg', () => {
    expect(testee.msg).toBe('hello');
  });
});

results in error:

Error: Error invoking TaskQueue. Check the inner error for details.
inner error: TypeError: 'undefined' is not a constructor (evaluating 'new     (window.MutationObserver || window.WebKitMutationObserver)(callback)')

Not sure I need task queue imported here, but did that anyway to try to fix the error.

I am initialising the DOM elsewhere in code with:

import {initialize} from 'aurelia-pal-browser';
initialize();

Getting more done in GitHub with ZenHub

Hola! @arhik has created a ZenHub account for the aurelia organization. ZenHub is the leading team collaboration and project management solution built for GitHub.


How do I use ZenHub?

To get set up with ZenHub, all you have to do is download the browser extension and log in with your GitHub account. Once you do, you’ll get access to ZenHub’s complete feature-set immediately.

What can ZenHub do?

ZenHub adds a series of enhancements directly inside the GitHub UI:

  • Real-time, customizable task boards for GitHub issues;
  • Burndown charts, estimates, and velocity tracking based on GitHub Milestones;
  • Personal to-do lists and task prioritization;
  • “+1” button for GitHub issues and comments;
  • Drag-and-drop file sharing;
  • Time-saving shortcuts like a quick repo switcher.

Add ZenHub to GitHub

Still curious? See more ZenHub features or read user reviews. This issue was written by your friendly ZenHub bot, posted by request from @arhik.

ZenHub Board

Discussion: interesting task-queue usage

So I was reviewing Aurelia code for #12 and this piece of code, found in repeat.js got me thinking:

handleInnerCollectionMutated(collection, changes) {
  // guard against source expressions that have observable side-effects that could
  // cause an infinite loop- eg a value converter that mutates the source array.
   if (this.ignoreMutation) {
    return;
  }
  this.ignoreMutation = true;
  let newItems = this.sourceExpression.evaluate(this.scope, this.lookupFunctions);
  this.observerLocator.taskQueue.queueMicroTask(() => this.ignoreMutation = false);
   // call itemsChanged...
  if (newItems === this.items) {
    // call itemsChanged directly.
    this.itemsChanged();
  } else {
    // call itemsChanged indirectly by assigning the new collection value to
    // the items property, which will trigger the self-subscriber to call itemsChanged.
    this.items = newItems;
  }
}

I understand the objective here but it doesn't feel right to me.

First it doesn't protect against all circular changes. Because if a computed property is in the cycle this won't work, as the computed property itself will be queued after the ignoreMutation reset.

Second, the problem at hand is not unique to this binding. In fact any code might trigger an evaluation that may have side effects that may create a cycle. Cycles in the dependencies are coding errors but they are sometimes unintended and it's good to have some protection against them. But I feel like the code above is too narrow and specific enough. To quote the comment: any binding can have a value converter.

Random thought: what about limiting the number of iterations in the micro-task loop?

Update Bower dependencies

aurelia-task-queue requires aurelia-pal#^0.3.0 rather than aurelia-pal#1.0.0-beta.1, which creates conflict that we have to manually resolve when using bower update.

Broken in IE10

Hello, I was wondering if there is a way to get TaskQueue to work in IE10 on a simple Hello World app. I tried using the solution found on http://eisenbergeffect.bluespire.com/aurelia-update-with-decorators-ie9-and-more/. My previous error was: "Error: Error invoking TaskQueue. Check the inner error for details." now with this 'fix' from the URL above, I am getting: "SCRIPT28: Out of stack space es6-collections.js, line 1 character 44". Can someone please try to make a clean slate Aurelia app and make it work for IE10? Any thoughts or ideas?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.