Giter Site home page Giter Site logo

wilk / microjob Goto Github PK

View Code? Open in Web Editor NEW
2.0K 28.0 47.0 2 MB

A tiny wrapper for turning Node.js worker threads into easy-to-use routines for heavy CPU loads.

Home Page: https://wilk.github.io/microjob/

License: MIT License

TypeScript 100.00%
thread nodejs threading multithreading jobs

microjob's Introduction

Microjob

npm version Build Status Coverage Status Dependencies

A tiny wrapper for turning Node.js threads in easy-to-use routines for CPU-bound.

Introduction

Microjob is a tiny wrapper for Node.js threads and is intended to perform heavy CPU loads using anonymous functions.

So, Microjob treats Node.js threads as temporary working units: if you need to spawn a long-living thread, then you should use the default API.

From version v0.1.0 microjob uses a Worker Pool ๐ŸŽ‰

Microjob follows the same line of the original Node.js documentation: use it only for CPU-bound jobs and not for I/O-bound purposes. Quoting the documentation:

Workers are useful for performing CPU-intensive JavaScript operations; do not use them for I/O, since Node.jsโ€™s built-in mechanisms for performing operations asynchronously already treat it more efficiently than Worker threads can.

Microjob can be used with Node.js 12+ without flag. With Node.js 10.5+ you need the --experimental-worker flag activated, otherwise it won't work.

More details explained in: Microjob: a tiny multithreading library for Node.js

Installation

Via npm:

$ npm install --save microjob

Quick Example

(async () => {
  const { job, start, stop } = require("microjob");

  try {
    // start the worker pool
    await start();

    // this function will be executed in another thread
    const res = await job(() => {
      let i = 0;
      for (i = 0; i < 1000000; i++) {
        // heavy CPU load ...
      }

      return i;
    });

    console.log(res); // 1000000
  } catch (err) {
    console.error(err);
  } finally {
    // shutdown worker pool
    await stop();
  }
})();

Features

  • ๐Ÿ›ข๏ธ Worker Pool
  • ๐Ÿฅ auto self-healing
  • ๐Ÿ™Œ easy and simple
  • ๐Ÿ•” supports both sync and async jobs
  • ๐Ÿ›ก๏ธ huge test coverage
  • ๐Ÿ“œ well documented

Documentation

Dive deep into the documentation to find more examples: Guide

Known Issues

Known Limitations

microjob's People

Contributors

amilajack avatar darky avatar deepsourcebot avatar haidy777 avatar larshp avatar pungggi avatar randy5235 avatar silversonicaxel avatar tomjw64 avatar wilk avatar yovanoc 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  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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

microjob's Issues

Add History to docs

Would like to propose adding a history file to repo so we can spot changes and get help with breaking changes.

Persistent context

Need ability to pass some context firstly and then, it will be always available in workers pool.
For example, CPU-intensive geo task - check point in polygons.
Polygons so weight and every time serialize - deserialize it so expensive.
Would be better to pass it firstly

await job(() => {
}, {persistentCtx: {polygons: [/* many-many polygons */]}});

And then on every job execute it always accessible:

await job(() => {
  polygons // it accessible here yet.
  
}, {data: {point: [12.3434, 56.3434]}});

Worker pool self healing

Currently, when a worker dies or when the user stops the worker pool, the system does nothing to restore the situation.
Here the feature would be having a self healing worker pool that spawns again dead workers and exports a "restart" or "setup" method to actually restart the pool.

Functions in nested objects are not passed.

const ctxPass = { foo: 'bar', func: () => 'this is func' };

// Working, but I'm confused because it doesn't match the parent variable name.
await job(() => {
  // @ts-ignore
  return func();
}, { ctx: ctxPass });

// Not Working
await job(() => {  
  return ctxPass.func();
}, { ctx: { ctxPass } });

https://github.com/wilk/microjob/blob/master/src/worker-pool.ts#L89
The function disappears from JSON.stringify.

const ctx = {
  test: { depth: { foo: 'bar', func: () => 'this is func' } },
  testFunc: () => 'testFunc()' };

function convert(ctx) {
  let ret = '{';
  for (let [key, val] of Object.entries(ctx)) {
    if (typeof val === 'function') {
      val = val.toString();
    } else if (Array.isArray(val)) {
      val = JSON.stringify(val);
    } else if (typeof val === 'object') {
      val = convert(val);
    } else {
      val = `'${val}'`;
    }

    ret += `${key}:${val},`;
  }
  ret += '}';

  return ret;
}

console.log(`let test = ${convert(ctx)}`);
// let test = {test:{depth:{foo:'bar',func:() => 'this is func',},},testFunc:() => 'testFunc()',}

Reference https://stackoverflow.com/a/57668208

What do you think about this?

microjob method decorator

microjob could export a decorator used on class methods.
For instance:

const { thread } = require('microjob')

class Executor {
  @thread(config)
  execute(data) {
    // configured using config
    // heavy cpu load on data
    // capability to use "this"
  }
  
  @thread(config)
  static compute(data) {
    // configured using config
    // heavy cpu load on data
  }
}

const executor = new Executor()

// both methods are executed in a separated thread
await executor.execute(data)
await Executor.compute(data)

In this way, it should be easier to define "job methods" in a declarative way.

Feasibility test needed before all.

  • implement @thread decorator for instance method
  • implement @thread decorator for class method
  • implement test for @thread decorator (instance method)
  • implement test for @thread decorator (class method)
  • add @thread decorator to docs

Take longer time than native worker_thread

microjob version:

15375.269ms

(async () => {
  const { job, start, stop } = require("microjob");

  try {
    // start the worker pool
    console.time("microjob");
    await start();

    // this function will be executed in another thread
    const res = await job(() => {
      let i = 0;
      const result = [];
      const threadCount = 4;
      const totalDataLength = 2000000;
      for (i = 0; i < threadCount; i++) {
        // heavy CPU load ...
        const crypto = require("crypto");
        const sha256 = (s) => crypto.createHash("sha256").update(s).digest();
        const shaArray = Array.from(Array(totalDataLength / threadCount)).map((num) =>
          sha256(String(num))
        );
        result.push(shaArray);
      }
      return result;
    });

    console.log(res);
    console.timeEnd("microjob");
  } catch (err) {
    console.error(err);
  } finally {
    // shutdown worker pool
    await stop();
  }
})();

Node.js native multi-thread version

5311.715ms

const { Worker } = require("worker_threads");
const path = require("path");
console.time('thread')
let workerPool = [];
const threadCount = 4;
const totalDataLength = 2000000;
for (let i = 0; i < threadCount; i++) {
  const workerInstance = new Promise((resolve, reject) => {
    const worker = new Worker(path.resolve("./worker.js"));
    worker.on("message", ({ data }) => {
      resolve(data);
    });
    worker.postMessage({
      arrayLength: totalDataLength / threadCount,
    });
  });
  workerPool.push(workerInstance);
}
Promise.all(workerPool)
  .then((values) => {
    console.log(values);
    console.timeEnd('thread')
  })
  .catch((err) => {
    console.log(err);
  });
const { parentPort } = require("worker_threads");
const crypto = require("crypto");
const sha256 = (s) => crypto.createHash("sha256").update(s).digest();

parentPort.on("message", ({ arrayLength }) => {
  const shaArray = Array.from(Array(arrayLength)).map((num) => sha256(String(num)));
  parentPort.postMessage({
    data: shaArray,
  });
});

unsupported in {zeit/pkg}

when i convert my app into exe file, return {worker pool has more than 10 worker} in windows server...

Use Generics instead of `any`

Big thanks to the TypeScript rewrite. Stumbled upon the interface and thinking that this can be improved to leverage the power of the language.

export interface Config {
ctx?: any
data?: any
}

I prefer here to accept Generics instead of using any:

// User can defined its own Generics
// TypeScript also accepts default types, but this requires certain version to support this IIRC.
export interface Config<T = {}, U = {}> {
  ctx?: T;
  data?: U;
}

function is not defined when using async function using babel and Typescript in development

Hi,

I'm using Babel and Typescript in development, which I compile to JS for production. However, when trying to use microjob with an async function working locally I get a _functionName is not defined error.
Here is some simple code to reproduce:

//index.ts
import express from "express";
import {job, start} from "microjob";

const app = express();

app.get("/", async function(req: express.Request, res: express.Response) {
    await start();

    const test = async () => {
        return 'test';
    }

    // all the below fail with the async before the function name above
    // const result = await job(test);
    // const result = await job(() => {
    //     return test();
    // }, { ctx: { test }});
    const result = await job(async () => {
        return test();
    }, { ctx: { test }});
    // const result = await job(async () => {
    //     const response = await test();
    //     return response;
    // }, { ctx: { test }});

    res.send(result);
});

const port = 4790;
app.listen(port);
console.log(`view app at http://localhost:${port}`);

My package.json:

{
  "name": "test-microjob",
  "version": "1.0.0",
  "description": "",
  "main": "index.ts",
  "dependencies": {
    "@babel/core": "^7.9.0",
    "@babel/node": "^7.8.7",
    "@babel/plugin-transform-runtime": "^7.9.0",
    "@babel/preset-env": "^7.9.5",
    "@babel/preset-typescript": "^7.9.0",
    "babel-watch": "^7.0.0",
    "express": "^4.17.1",
    "microjob": "^0.7.0"
  },
  "devDependencies": {
    "@types/express": "^4.17.6"
  },
  "scripts": {
    "start": "babel-watch --inspect --extensions \".ts\" index.ts",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

.babelrc

{
  "presets": ["@babel/env","@babel/preset-typescript"],
  "plugins": [
    "@babel/plugin-transform-runtime"
  ]
}

My tsconfig.json

{
  "compilerOptions": {
    "target": "es2017",
    "allowJs": true,
    "skipLibCheck": false,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "isolatedModules": false,
    "jsx": "preserve",
    "outDir": "./build",
    "sourceMap": true
  },
  "include": [
    "index.ts"
  ]
}

Node version 12.6.2

To note: Using an async function passed

Thanks for any help in advance ๐Ÿ‘

ts-node support/example?

Great library! Love the minimalistic API and 0 dependencies!

I just tried to use it for a little bit more complex example and it failed immediately with such code:

await job(async accountId => {
  const account = await accountDao.getById(accountId)
  ...
}, {data: accountId})

What's happening here is that it's using some accountDao to fetch some thing from DB. This accountDao is imported above as:

import {accountDao} from '@src/accountDao'

2 problems:

  1. It can't find this module by such path (probably because @src is achieved using tsconfig-paths)
  2. Even if I change it to some relative path like ./src/accountDao or ../../src/accountDao - no luck.

My next try to is to:

require('ts-node/register')
const {accountDao} = require('./src/accountDao')

And then it's loaded! But then all its dependencies fail (cause they're still written in form of @src/....

Any advice? Is it just me or someone else also having such issue?

I just cannot imaging the use of this module without using some dependent files, which doesn't work right now for a typescript project..

unit tests / CI

We should add Travis CI to test every commit and PR of microjob.

While loop only run once inside worker thread

I played around with this library a bit to see how easy it is to use a worker thread. Whilst I was using it, I stumbled upon weird behavior where the while loop only run once inside the worker thread. Here is the minimal example to reproduce:

import { start, job } from "microjob";

(async () => {

  try {
    // start worker pool
    await start({ maxWorkers: 4 }); // you can also limit the available workers

    job(() => {
      while (true) {
        console.log("inside loop")
      }
    })

    // ...
  } catch (err) {
    console.error(err);
  }
})();

crashing on require in node10

$ node
> require('microjob')
Error: Cannot find module 'worker_threads'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
    at Function.Module._load (internal/modules/cjs/loader.js:507:25)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:20:18)
$ node -v
v10.11.0
$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.5 LTS
Release:	16.04
Codename:	xenial

__awaiter is not defined for async jobs

Synchronous jobs run fine but I'm getting this error when trying to run an async job:

19:51:02.213Z ERROR thingy (1.0.0): Unhandled promise rejection occurred.
    ReferenceError: __awaiter is not defined
        at eval (eval at <anonymous> (/thingy/node_modules/microjob/dist/worker.js:10:5), <anonymous>:8:29)
        at __executor__ (eval at <anonymous> (/thingy/node_modules/microjob/dist/worker.js:10:5), <anonymous>:18:16)
        at MessagePort.<anonymous> (/thingy/node_modules/microjob/dist/worker.js:12:27)
        at MessagePort.emit (events.js:196:13)
        at MessagePort.onmessage (internal/worker/io.js:68:8)

Example code:

public static async initialize () : Promise<void> {
		const threadPoolSize: number = 2;
		logger.info(`Initializing ${threadPoolSize} workers`);
		await start({ maxWorkers: threadPoolSize });

		const res: any = await job(async () => {
			const later: any = (delay: number) => new Promise((resolve: any) => setTimeout(resolve, delay));
			let i: number = 0;
			for (i = 0; i < 10; i++) {
				await later(1);
				for (let j: number = 0; j < 100; j++) {
					for (let k: number = 0; k < 100; k++) {}
				}
			}
			return i;
		});

		logger.info(`Done Initializing`, res);
	}

Any ideas?

worker pool with clustering

When the thread pool feature will be added,
a question that comes to mind, can the thread pool
be shared between multiple forked workers working
in a cluster.

for example having 4 cpus, each running a forked node process (using native nodejs clustering)
the worker pool should be for all 4 cpus and not per cpu, otherwise it will consume too much memory and resources.

best would be to configure a pool either local per cpu, or global, per machine (one pool for all cpus)

What's your take on this?

Clarification about using in http server

Hi, and thank you for that awesome work. The api is perfect for my use case (off loading some CPU intensive calcs).

Is there some guidance on how to setup a web server to use microjob?

for example, say I'm using koa. Should I use start() next to the import at the top of a module? This will cause the worker pool to be global. Or should I call await start() and await stop() on every request?

Thank you.

Hackernoon pictures are missing?

First of all, thanks for this repo.

The hackernoon article which contains interesting docs, might have lost all the picture links ? This is the only image I can see.

image

I don't know if this is intentional

Confused about starting multiple worker threads

I apologize, this is really a question and not an issue, but I can't seem to figure out if it's possible to spawn more than one worker thread using microjob.

I'm trying something like the following:

import { job, start, stop } from 'microjob';

(async () => {

  try {
    // start the worker pool
    await start();

    // this function will be executed in another thread
    const jobA = job(() => {
      console.log(`Worker ${process.pid} started`);
    });

    // this function should be executed in yet another thread
    const jobB = job(() => {
      console.log(`Worker ${process.pid} started`);
    });

    await Promise.all(jobA, jobB);

  } catch (err) {
    console.error(err);
  } finally {
    // shutdown worker pool
    await stop();
  }
})();

Every time I try something like this though, both worker threads console.log the exact same pid value. Am I doing something wrong? Or is this just a limitation with microjob?

Consider thread pool

First off, I really like the idea if this project. I noticed when reading the docs that a thread pool is recommended and I think it might be a good feature for this library to avoid creating a new thread for every bit of work.

Quote from docs:

In practice, it is strongly recommended to use a pool of Workers for these kinds of tasks, since the overhead of creating Workers would likely exceed the benefit of handing the work off to it.

function could not be cloned

I'm using a function inside my job since it is from a library i use for compression and it is the CPU intensive task i need to allocate to the worker thread. this is returning an Error for me
saying ' The function{ } cannot be cloned '.
What does this mean & how do i solve this?

If this is not the proper place to ask this and you'd prefer i ask this on stack overflow, i'll bring this down.

ReferenceError: immutable_1 is not defined when using 'immutable' library

Hi,

This library looks really cool. Unfortunately I hit a snag when I try to use 'immutable'. For example:

  await start();

  const res1 = await job(() => {
    let i = 0;
    for (i = 0; i < 1000000; i++) {}
    let config = OrderedMap(fromJS({}));
    return i;
  });

In the above example, it fails immediately with "immutable_1 is not defined". Is this a known limitation due to the way Immutable was written (using factories)?
Thanks!

Security issue: injecting any JS code

If context variable value is received from its remote user (e.g. HTML form), and I would expect this will happen often, it is easy to execute any JS expression:

Here's your example (a bit reduced):

(async () => {
  const { job } = require('microjob')
    // this function will be executed in another thread
  const counter = 1000000
  const res = await job(() => {
    return counter
  }, {ctx: {counter:process.env.DATA_FROM_USER}})

})()

Now running it with:

$ DATA_FROM_USER="Z';console.log(\"hi\");'" node  --experimental-worker .\test.js
hi

DATA_FROM_USER here simulates remote data

Suggestion: CPU bound example

Hi,
The Sync example runs in a different thread as described, and I did not succeed getting the async example working. However, it looks like both examples are not limited by CPU, suggest adding an example which utilites all cores.

Following is what I've come up with:

let res = [];
for (let j = 0; j < 10000; j++) {
  res.push(job(() => {
    let total = 0;
    for (let i = 0; i < 10000; i++) {
      total = total + Math.floor(Math.random() * Math.floor(100));
    }
    return total;
  }));
}
console.log(await Promise.all(res));

Microjob doesn't work with typescript

When I try to run the example using typescript:

(async () => {
    try {
        // start worker pool
        await start();

        // this function will be executed in another thread
        const res = await job(async () => {
            let i = 0;
            for (i = 0; i < 1000000; i++) {
                for (let j = 0; j < 1000000; j++) {
                    for (let k = 0; k < 1000000; k++) {
                        http.get("www.google.it");
                    }
                }
            }
            return i;
        });
        console.log(res); // 1000000
    } catch (err) {
        console.error(err);
    }
})();

I get:

ReferenceError: tslib_1 is not defined
    at eval (eval at <anonymous> ([worker eval]:11:5), <anonymous>:8:37)
    at __executor__ (eval at <anonymous> ([worker eval]:11:5), <anonymous>:10:12)
    at MessagePort.<anonymous> ([worker eval]:13:27)
    at MessagePort.[nodejs.internal.kHybridDispatch] (internal/event_target.js:399:24)
    at MessagePort.exports.emitMessage (internal/per_context/messageport.js:18:26)
    at MessagePort.callbackTrampoline (internal/async_hooks.js:130:17)

If I run the code from javascript it works. Is there a solution for this? โ˜๏ธ

Will requiring module in microjob harm performance?

(async () => {
  const { start, job } = require("microjob");

  try {
    // start worker pool
    await start();

    // this function will be executed in another thread
    const res = await job(async () => {
      const module = require('some-module')
      return module.someFunction()
    });

    console.log(res);
  } catch (err) {
    console.error(err);
  }
})();

I'd like to know will the require in the job make the performance really bad?

typescript declarations

Hi,
Suggest adding "declaration": true, under compilerOptions in tsconfig.json, this will automatically generate the d.ts files, so that the index.d.ts file is not needed anymore

docs: async job

From GUIDE.md:
"An asynchronous job is a task with at least one async call: for instance, a query to a DB, a HTTP request, a file system call, etc, and of course plus a heavy CPU load"

from https://nodejs.org/api/worker_threads.html#worker_threads_worker_threads
"do not use them for I/O, since Node.jsโ€™s built-in mechanisms for performing operations asynchronously already treat it more efficiently than Worker threads can"

so suggest removing "a file system call" in GUIDE.md

(sorry about the spam today)

can't call functions in workers

Not sure if it's still within the scope of this project.

I tried making my worker function call other functions but it didn't work. Passing them through ctx didn't work either (tho i guess functions can't be passed to context).

Is it possible to define functions that the worker is able to call?

Single file build

Hello,

I'm working on a big project in typescript and microjob could help me a lot, I already implements the things I have to put on worker threads and it's a huge improvement while I'm testing this in dev mode.

But the problem is that the production mode is going through webpack to build a single final js file to run with node. But for microjob I see that we need this .js file in the node_modules.

So I ask if there is a way to concat files, or if you are considering this for futures versions?

Really good job and thanks for this lib otherwise

High core count CPU causes misleading memory leak warning

When testing this library out I received the warning
MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 17 error listeners added. Use emitter.setMaxListeners() to increase limit

I puzzled over why my code was adding so many listeners, before realising it was caused by microjob. I was about to dismiss microjob and look for an alternative when I realised that the default numbers of workers is equal to the number of OS threads. My machine has 16, which explains this behaviour.

I see that I can use MAX_WORKERS to set the number lower, but others may not realise this, or even realise that their core count is what's causing the issue.

Perhaps the number of listeners can be increased on >10 core CPUs (as node will warn after 10 listeners), or a custom runtime warning could be included explaining the cause.


Additional request, could we perhaps have a way to set max workers via an argument rather than requiring an environment variable? This would allow me to include it in an application configuration file instead of needing to modify my launch command.

Can't install with node 10.x

Hi, I found your lib today, it look very awesome!, but when I tried to install it, yarn do not allow me because you changed the min versions to 12.6.0:

error [email protected]: The engine "node" is incompatible with this module. Expected version ">=12.6.0". Got "10.16.0"
error Found incompatible module.

if it is not because a technical reason, can you delay this constrain until 2019-10-22 when Node12 start its LTS? https://nodejs.org/en/about/releases/

Thanks and have a nice day!

save data to mongoose

global.config=require('./config.js')
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
mongoose.connect(config.database.url , { useNewUrlParser: true });


(async () => {
    const { start, job } = require("microjob");

    try {
        // start worker pool
        await start();

        // this function will be executed in another thread
        const res = await job(async () => {
            let i = 0;
            console.log(1);
            let User = require('./model/user');
            console.log(2);
            let newuser=new User({nickName:"salam"});
            console.log(3);
            await newuser.save();
            console.log(4); //------------------------------ NOT SHOW---------------------------------------------

            return i;
        });

        console.log(res); // 1000000
    } catch (err) {
        console.error(err);
    }
})();

Error running example code

When running the example code, I receive the following error:
{ Error: Cannot find module '/Users/rharrington/src/temp/microjobtest/src/worker.js' at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15) at Function.Module._load (internal/modules/cjs/loader.js:507:25) at Function.Module.runMain (internal/modules/cjs/loader.js:742:12) at MessagePort.port.on (internal/worker.js:445:27) at MessagePort.emit (events.js:182:13) at MessagePort.onmessage (internal/worker.js:66:8) code: 'MODULE_NOT_FOUND' }

Details included below:

OS: OSX v10.13.4

Node: v10.10.0

command: node --experimental-worker index.js

index.js:
(async () => {
const { job } = require('microjob')
try {
// this function will be executed in another thread
const res = await job(() => {
let i = 0
for (i = 0; i < 1000000; i++) {
Array.from({length: 1000}, () => Math.floor(Math.random() * 1000)).sort()
}
return i
})
console.log(res) // 1000000
} catch (err) {
console.error(err)
}
})()

package.json :
{
"name": "microjobtest",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"microjob": "^0.1.0"
}
}

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.