Code Monkey home page Code Monkey logo

task.js's Introduction

task.js Build Status Code Climate CDNJS

This module is intended to make working with blocking tasks a bit easier, and is meant to work in node as well as the browser.

LIVE DEMO 1

LIVE DEMO 2

It works by creating a worker pool, and sending tasks to workers that are not busy. If all workers are busy then the task gets queued until there is room.

install

# node
npm install task.js

# usage
import Task from 'task.js';

or just grab the cdnjs hosted version directly

usage

let task = new Task({/* options */});

await task.run(
	number => Math.pow(number, 2),
	2
); // returns 4

options

  • debug = false [Boolean] enables verbose event logging
  • logger = console.log [Function] allows you to override logging, or handle the events in a custom manner
  • workerType = undefined [String] allows you to override the worker type, currently is only useful in node.js with worker_threads, or fork_worker
  • warmStart = true [Boolean] creates all workers before running tasks
  • maxWorkers = 1 [Integer] you can use os.cpus().length or navigator.hardwareConcurrency to obtain the system core count (do not set this value higher than the amount of cores on the machine)
  • workerTaskConcurrency = 1 [Integer] useful if you are running async tasks, it allow allow a worker to handle multiple tasks at the same time
  • taskTimeout = 0 [Integer] useful when trying to prevent long running tasks from stalling. It will kill the offending working and recreate another one, and reissue its tasks to the pool.
  • requires [Object] key value require object like the following {qs: 'querystring'}, all keys would become globals. This is only supported in Node.js!
  • globals = {} [Object] sets global variables in all workers {foo: 'bar'}, would set foo to 'bar'
  • initialize = undefined [Function] you can use global.foo = 'bar' within this function to
  • env [Object] allows you to set/override the workers env variables (by default they are inherited from the main process) This is only supported in Node.js!

The Function/String you pass into .run or .wrap is ran inside of the workers context. So it will NOT have access to your main threads variable scope. If you would like to pass information into the task you need to use function arguments, or task globals.

task.wrap (Func|String)

You can wrap a function if the method signatures match, and it doesn't rely on any external variables.

// non async
let pow = task.wrap(number => Math.pow(number, 2));

console.log(await pow(2));

But keep in mind that your function cannot reference anything inside of your current scope because it is running inside of a worker.

task.run(Func|String, [...arguments])

Below is an example of using a transferable

// create buffer backed array
var array = new Float32Array([1,3,3,7]);

let result = await task.run(
	array => {
		// do intensive operations on your buffer
    	return array.buffer;
	},
	array,
	Task.transferables(array)
);

task.terminate

When you run terminate it will destroy all current workers in the pool, and throw an error on all outstanding work.

task.terminate();

globals in workers

You can initialize a task instance to have predefined data from your main thread, or generated within the worker.

let data = {foo: 'foo'};

let task = new Task({
	globals: {
		data: data,
		bar: 'bar'
	}
});

await task.run(() => {
	console.log(data.foo + bar);
});

You can also use initialize to define common methods in the worker scope as well.

task.defaults({
	globals: {
		foo: 'foo',
		bar: 'bar'
	},
	initialize: () => {
		global.fooBar = () => {
			return foo + bar;
		}
	}
});

await task.run(() => {
	console.log(fooBar());
});

Keep in mind that it is ok to have a slow initialize, no work will actually be processed until there is a fully initialized worker.

external libraries

You can import libraries into your workers by providing requires object in the Task initialization.

browser

Uses importScripts to import the library to the worker so it MUST expose the library on the global scope.

requires: {
	// key = the global name of this library (will fail if it does not match)
	// value = the path/url of the library
	saw: 'https://cdnjs.cloudflare.com/ajax/libs/string-saw/0.0.42/saw.js'
}

node

Uses the standard requires method to import libraries

requires: {
	// key = the var you want to name the library
	// value = the name of the npm library
	saw: 'string-saw'
}

async tasks

Tasks can use async, and return promises

let task = new Task({
	requires: {
		request: 'request-promise'
	}
});

let workerRequest = task.wrap(
	async options => {
		let response = await request(options);
		// do something CPU intensive with the response
		return response;
	}
);

console.log(await workerRequest({url: 'https://www.google.com'}));

shared memory

There are two ways to use shared memory

SharedArrayBuffer

supported in browser:web_workers, and node:worker_threads

When using this please make sure to use Atomics, which is supposed in both the browser, and node.

let sharedArray = new Uint8Array(new SharedArrayBuffer(2));

await task.run(
	sharedArray => Atomics.add(sharedArray, 0, 1),
	sharedArray
);

sharedArray[1] = 2;

console.log(sharedArray); // Uint8Array [ 1, 2 ]

Transferable Arrays

supported in browser:web_workers, and node:worker_threads

To use transferables in task.js all you need to do is pass in Task.transferables([...Array/Buffer]) as the methods last argument with references to the same transferable arrays you passed into the method. If you do not pass this last argument in then it will be treated like a standard array, and copied instead of transferred.

let transferableArray = new Uint8Array(1);

let result = await task.run(transferableArray => {
	transferableArray[0]++;
	return transferableArray;
}, transferableArray, Task.transferables(transferableArray));

Task.transferables(...Array/Buffer)

You can pass in buffers, or buffer backed arrays. Behind the scenes task.js will pass the array buffer, according to the tranferables interface.

Task.transferables(transferableArray, transferableArray.buffer)

task.js's People

Contributors

icodeforlove avatar wain-pc avatar pkoretic avatar peterdavehello avatar

Stargazers

Fintechee avatar Dong Nan avatar Paul Day avatar  avatar Jesus Urrutia avatar Çağatay avatar Alexandr Stets avatar  avatar  avatar Jonathan Turnock avatar Davyd_Zaichenko avatar Sua Le avatar buzz avatar Tausif avatar Jens Hunt avatar  avatar Artur avatar Amman Pasha avatar Eliaz Bobadilla avatar Florian Schulz avatar  avatar Max Tropnikov avatar  avatar Grigorii K. Shartsev avatar k3v avatar Mirko Chialastri avatar S M Samnoon Abrar avatar Md. Shahidul Islam avatar Thomas avatar Tej avatar  avatar  avatar JITAO avatar Jkyo Chen avatar Pankaj avatar JMA avatar Josh Girvin avatar  avatar  avatar Julio Gorge avatar Kenneth Bruskiewicz avatar Dariusz Filipiak avatar Mohammad H. Sattarian avatar Pedram Marandi  avatar Farzad Yousefzadeh avatar 喜鸽小宝 avatar Alexandru Ionascu avatar B. Branchard avatar  avatar Boniface Mbaria avatar Vali Draganescu avatar Sergii Danilov avatar  avatar Eric Wallin avatar Graham McNeill avatar Abhishek Jha avatar  avatar Aleksandr Bogdanov avatar  avatar Gabriel Rimes avatar Kevin.Y avatar Cat  avatar  avatar Brian Boucheron avatar normal100 avatar Drew Synan avatar Vlado avatar Salvatore Ravidà avatar yangy avatar lxp avatar jinfeng.li avatar Justin avatar LEI avatar Etienne Martin avatar  avatar Tarun Chaudhry avatar Lobito avatar TheGreatRambler avatar Hasan Bayat avatar mika avatar Artur Nasiadko avatar Kent Gruber avatar Patricio López Juri avatar Adrian Li avatar Rafael Lüder avatar Michael Lawrence avatar Josh Miller avatar CallMeLaNN avatar C.P avatar danielsdesk avatar Andrew Camilleri avatar Anderson Araujo avatar Jose Ignacio Santa Cruz G. avatar  avatar Kamil Zaleski avatar Daniel Schmidt avatar liujian zhang avatar Arthur Guiot avatar Ryan Lester avatar Shahar Soel avatar

Watchers

Imran Ansari avatar  avatar  avatar James Cloos avatar Xianliang Wu avatar Marius Podean avatar  avatar

task.js's Issues

Node.js webworker-threads?

At the moment we are using fork to get away with the simulation of a thread.

The webworker-threads module seems like the perfect module to utilize for workers.

Benchmarks are needed in order to make the switch (#16).

Warm start option

As it stands when task.js starts it doesn't spawn any workers, instead the workers are spawned when needed, and reused if idle. The only way at the moment you reach the max workers is if all workers are tied up at the same time.

Worker globals?

This comes with limitations of course.

But if you are in the situation where you have a large set of data or just want to share functions across your workers you may be able to do something like this.

task.global({
    data0: [1,2,3,4,5],
    data1: [1,2,3,4,5],
    concatData: function () {
        return global.data0.concat(global.data1);
    }
}).then(function () {
// globals are initialized against all active workers, and will continue to be initialized in all new workers
});

usage after initialization

var concatAllData = task.wrap(function (data) {
    return data.concat(global.concatData());
});

getAllData([1,2,3,4,5]).then(function (data) {
    console.log(data);
});

add env support to Node.js workers

Since fork, and worker_threads both support passing in a custom process.env.

I as thinking the following interface would work fine

new Task({
    env: {...process.env, NODE_ENV: 'test'}
});

Requires inside of tasks, and general data sharing

This needs to be improved for the node.js side of things.

This library was originally developed for client-side only work and later ported to use nodes .fork method.

With the addition of worker_threads we now have thread support which opens a few doors.

So I would like to improve the require interface, as well as the data passing interface.

I personally have been abusing the initialize method to stuff requires in on top of the globals object. Which works fine, but seems quite messy.

Update globals

Need a nice way to update all workers globals.

It may make sense just to kill all workers and reintroduce them.

SharedArrayBuffer tests

This should be coming soon, just saw it land in v8 as a harmony flag.

I'm not really sure where node stands on this. I really do wish they would embrace the idea of Transferables or SMB's.

Performance: Large messages / Large globals

As it stands the client-side version performs much better than nodes for sending large objects, as for message speed node.js wins.

node.js

At the moment if you need to send large data you just shouldnt, its more performant to just write it to disk and read from the worker, and load it back from disk.

I read somewhere that we can use stdin/stdout streams? Maybe theres a nice place to add some custom serialization / deserialization logic using streams?

Another option is to look into node-shm (https://www.npmjs.com/package/node-shm-buffer)

client-side

When using transferable object most this is solved, but even without them its not too bad.

How to use dependencies in my worker task?

Importing another dependencies inside of a worker task is broking the code. I think that when is passed to Blob method, the webpack don't know, how to deal with imports and requires.

Error:
Can't find variable: webpack_require

Async work?

This doesn't make sense for WebWorkers, but this may make sense for SharedWorkers.

How to use on server side?

I try

var task = require('task');

function pow(number) {
    return Math.pow(number, 2);
}

var powTask = task.wrap(pow);
powTask(2).then(function (squaredNumber) {
    console.log(squaredNumber);
});

But get error

"task.wrap is not a function"

I also try

import Task from 'task';

const task = new Task();

task.run({
    arguments: [2],
    function: (number) => {
        console.log('pow:', number);
        return Math.pow(number, 2);
    }
}).then(
    (result) => {
        console.log('done:', result);
    },
    (err) => {
        console.log('err:', err);
    }
);

But only done: true is print?

Any hint?

Add logging/debugging options

It would be nice to see a heartbeat for current workers / current task queue.

This will help you figure out if your jobs are stacking up, or maybe enable warnings if queue exceeds workers by ~20%. Depending on how you write your async code you can lock yourself up if you have too many jobs running, and callbacks waiting on other things to complete ;).

Update web demo

The demo should be more visual.

Maybe even create a LIVE visual debugger which maps out the workers, and shows the jobs moving to the workers and completing.

Also have the ability to switch between shared memory and non-shared memory, a good example would be some sort of image processing.

added importScripts support to requires

Seems like since we can do the following to mirror node

requires: {
    saw: 'https://cdnjs.cloudflare.com/ajax/libs/string-saw/0.0.42/saw.js'
}

Since importScripts does not actually return any values we can at least check if the keys exist in the global scope after the import.

Deprecate: callbacks, Task.decorate, task.setGlobals, Task.defaults

It's safe to say that promises have won the async battle, and decorators are not necessary.

I would like to keep this library simple, and ass more focused as possible.

Which means tailoring task.js to creating a task runner, so removing .defaults method in favor of new Task({options...}), and forcing you to actually configure task.js beforehand.

And be ok with the idea of terminating task runners, and using multiple task runners, if needed.

task.js is light weight, so theres nothing wrong with having a few task.js instances as a background thread with a maxWorker count of 1, and a concurrency of 4 for a specific type of task.

add task.stats

for every worker pool we could return the following information

  • total workers
  • total failed tasks
  • total completed tasks
  • workers active
  • workers inactive
  • average workers active
  • min workers active
  • max workers active
  • tasks per second
  • ...

This would be helpful when trying to debug (before resorting to logs), or decide how big your pool should be.

Webpack loader

At the moment you will get an error if you try to use webpack to pull in the node version.

This happens because the node version has a require('os') in it to get defaults.

Need to figure out if we can defer loading of this module.

module.exports = function () {
    let os = require('os');
    return ...;
};

This is a hack but it rarely gets run in most situations (initially, and every-time .defaults is ran)

Add batching/map logic

var powTask = task.wrap(pow);

adding to a batch, with multiple argument sets

var batchPowTask = powTask.batch({size: 2});
// fill the batch up
for (var i = 0; i < 10; i++) {
    batchPowTask.add(i, 2);
}
batchPowTask
    .run()
    .then(function (results) {
    });

simple array batching

var batchPowTask = powTask.batch({size: 2}).map([1,2,3,4,5,6,7,8])

batchPowTask()
    .run()
    .then(function (results) {
    });

not really settled on an interface yet, but the idea is that behind the scenes an array of argument sets get sent to the worker as well as how to execute them.

Add opensauce testing

Currently its only running basic tests in phantom.js.

But i think it would be good practice to run the tests against a real browser matrix.

Better documentation

The current README is complicated.

We need a clear view of the API, and some working example code.

*WorkerProxy object naming, and extension

  • Worker needs to be renamed to GeneralWorker (because it squashes the reserved web namespace).
  • NodeWorkerProxy should extend TaskWorker and be called NodeWorker
  • WebWorkerProxy should extend TaskWorker and be called WebWorker

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.