Code Monkey home page Code Monkey logo

greenlet's Introduction

Greenlet

Greenlet npm travis gzip size install size

Move an async function into its own thread.

A simplified single-function version of workerize, offering the same performance as direct Worker usage.

The name is somewhat of a poor choice, but it was available on npm.

Greenlet supports IE10+, since it uses Web Workers. For NodeJS usage, Web Workers must be polyfilled using a library like node-webworker.

Installation & Usage

npm i -S greenlet

Accepts an async function with, produces a copy of it that runs within a Web Worker.

โš ๏ธ Caveat: the function you pass cannot rely on its surrounding scope, since it is executed in an isolated context.

greenlet(Function) -> Function

โ€ผ๏ธ Important: never call greenlet() dynamically. Doing so creates a new Worker thread for every call:

-const BAD = () => greenlet(x => x)('bad') // creates a new thread on every call
+const fn = greenlet(x => x);
+const GOOD = () => fn('good'); // uses the same thread on every call

Since Greenlets can't rely on surrounding scope anyway, it's best to always create them at the "top" of your module.

Example

Greenlet is most effective when the work being done has relatively small inputs/outputs.

One such example would be fetching a network resource when only a subset of the resulting information is needed:

import greenlet from 'greenlet'

let getName = greenlet( async username => {
    let url = `https://api.github.com/users/${username}`
    let res = await fetch(url)
    let profile = await res.json()
    return profile.name
})

console.log(await getName('developit'))

๐Ÿ”„ Run this example on JSFiddle

Transferable ready

Greenlet will even accept and optimize transferables as arguments to and from a greenlet worker function.

Browser support

Thankfully, Web Workers have been around for a while and are broadly supported by Chrome, Firefox, Safari, Edge, and Internet Explorer 10+.

If you still need to support older browsers, you can just check for the presence of window.Worker:

if (window.Worker) {
    ...
} else {
    ...
}

CSP

If your app has a Content-Security-Policy, Greenlet requires worker-src blob: and script-src blob: in your config.

License & Credits

In addition to the contributors, credit goes to @sgb-io for his annotated exploration of Greenlet's source. This prompted a refactor that clarified the code and allowed for further size optimizations.

MIT License

greenlet's People

Contributors

bogas04 avatar dcthetall avatar developit avatar johnsonjo4531 avatar joshuaquek avatar karol-majewski avatar mattsidor avatar nem035 avatar spkellydev avatar styfle avatar sylvaindumont 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

greenlet's Issues

Cannot use spread operator

Issue

Hi, I've been following your work on this project since the beginning and now I'm ready to give it a try.

I'm using greenlet inside my Vue 2 application and in one particular case I've the following error :

Uncaught (in promise) ReferenceError: [_PATH_]_node_modules_babel_runtime_helpers_esm_objectSpread2__WEBPACK_IMPORTED_MODULE_2__ is not defined

It's seems it miss a dependency to transpile the spread operator. I don't know if it is a Vue specific issue or something more global

Sep to reproduce

Inside a vue compoenent :

import greenlet from 'greenlet';

const fnSomeWorkerAction = greenlet((itemsToWorkWith) => {
  return itemsToWorkWith.map((item) => {
    return {
      ...item,
    };
  });
});

export default {
  data() {
    items: Array.from(Array(100)).map((item, idx) => ({ id: idx, name: `Plop ${idx}` }),
    fnSomeWorkerAction
  },
  mounted() {
    this.fnSomeWorkerAction(this.items);
  }
}

Action required: Greenkeeper could not be activated ๐Ÿšจ

๐Ÿšจ You need to enable Continuous Integration on all branches of this repository. ๐Ÿšจ

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because it uses your CI build statuses to figure out when to notify you about breaking changes.

Since we didnโ€™t receive a CI status on the greenkeeper/initial branch, itโ€™s possible that you donโ€™t have CI set up yet. We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

If you have already set up a CI for this repository, you might need to check how itโ€™s configured. Make sure it is set to run on all new branches. If you donโ€™t want it to run on absolutely every branch, you can whitelist branches starting with greenkeeper/.

Once you have installed and configured CI on this repository correctly, youโ€™ll need to re-trigger Greenkeeperโ€™s initial pull request. To do this, please delete the greenkeeper/initial branch in this repository, and then remove and re-add this repository to the Greenkeeper Appโ€™s white list on Github. You'll find this list on your repo or organizationโ€™s settings page, under Installed GitHub Apps.

ESLint/TypeScript: Any way to mark the function as external/scope-less?

This piece of code passes every static analyzer, ESLint and TS because they don't know that the arrow function is run in a different context:

import greenlet from 'greenlet'

const myFavNumber = 43;

const getFav = greenlet( async () => {
    return myFavNumber
});

Are you aware of any way to mark the function in a way that linters/parsers know myFavNumber is not defined in the function?

Edge issue. SCRIPT5022: SecurityError

Hi guys. Have tried to use greenlet for my project but got security error in the Microsoft Edge(40.15063.674.0).
security_error
Any help would be very appreciated!

Thanks in advance

_ref is not defined

vue2 ๏ผŒ

  methods: {
    async btnTest() {
      let getName = greenlet( async username => {
        let url = `https://api.github.com/users/${username}`
        let res = await fetch(url)
        let profile = await res.json()
        return profile.name
      })
      console.log(await getName('developit'))
    },
  },

Refused to create a worker from blob

Refused to create a worker from 'blob:http://localhost:8080/87c22970-8fee-44d9-982e-00aafe130b22' because it violates the following Content Security Policy directive: "default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap: content:". Note that 'worker-src' was not explicitly set, so 'default-src' is used as a fallback.

Current content security policy: default-src * 'self' 'unsafe-inline' 'unsafe-eval' data: gap: content:

I don't have the slightest idea of how content security policy works, so, it would be nice some light in the README.md =)

Why do I get a return of undefined?

I am flummoxed by the following, while I can log exactly the result I am looking for, it comes back as undefined in my return

const combo = {}
combo.cprefixes = JSON.parse(JSON.stringify(this.prefixes))
combo.cmyn = this.myn
console.log('combo', combo) // looks good here

const getAnswers = greenlet(async combo => {
  const pkeys = Object.keys(combo.cprefixes)
  const promises = []
  console.log(combo) // looks good here too
  for (const key of pkeys) {
	const url = 'https://my.data.api?myn=' + cmyn + '&prefix=' + combo.cprefixes[key].prefix
	const res = await fetch(url).then(response => response.json())
	promises.push(res)
  }
  console.log('promises:', JSON.parse(JSON.stringify(promises))) // looks good here too
  await Promise.all(promises).then(results => {
	const keys = Object.keys(results)
	console.log('green results: ', results) // looks good here too
	for (const key of keys) {
	  combo.cprefixes.find(x => x.prefix === results[key].data.prefix).name = results[key].data.name
	}
	console.log('modified prefixes:', combo.cprefixes) // looks good here too
	return combo.cprefixes
  })
})

await getAnswers(combo)
  .then((response) => {
	console.log('got a response?:', response) // undefined here, why?
  })

Make greenlet working with a pool of Promises

Hi guys, maybe I'm wrong and need to give it a better look at service workers but what I'm trying to accomplish is the following:

1- I'd like to pass to greenlet an array of promises like the Promise.all(promises) return.
2- Await for them inside the greenlet function.
3- returning the result to the main app.

This is my code:

          try {
            const promises = validationAuto(chunk, workarea, criteria, test_groups); 
            if (window.Worker) {
              validationAutoWorkerized(promises);
            } else {
              await promises;
            }
          } catch (error) {
            console.log(error);
          }
const validationAuto = (chunk, workarea, criteria, test_groups) => {
  const validationPromises = chunk.map(id => {
    return new Promise((resolve, reject) => {
      postWorkareaValidationAuto(workarea, id, criteria, test_groups)
        .then(response => resolve(response))
        .catch(error => reject(error));
    });
  });
  return Promise.all(validationPromises);
};
const validationAutoWorkerized = greenlet(async promises => {
  const data = await promises;
  console.log(data);
  return data;
});

Any tip to accomplish it with greenlet? Thanks.

Support for terminating workers?

At the moment it doesn't appear greenlet supports terminating threads. From my experimentation it appears that even if workers are not referenced they do not seem to be destroyed:

spectacle b18024

After about 20 threads the client will become unresponsive / crash.

Being able to do something like:

import greenlet from 'greenlet'

let getName = greenlet( async username => {
    let url = `https://api.github.com/users/${username}`
    let res = await fetch(url)
    let profile = await res.json()
    return profile.name
})

console.log(await getName('developit'))
getName.terminate(); // Not sure if that's actually possible with the current setup

Would potentially help with the Web Worker lifecycle management.

Performance ?

Has any benchmark been done to help users know when using Greenlet is beneficial ? Also, knowing when not to use it would be a great gain.

Anyway, awesome work, thanks !

indexedDB is null in Firefox

Hello! Just noticed this problem with greenlet on Firefox (66.0.3); indexedDB is null in the greenlet execution context.

Reproduction case:

const callIndexedDB = greenlet(async (limit) => {
    console.log(indexedDB);
    // null
});

Also on Chrome (73.0.3683.103) it appears the success callback is never called:

const callIndexedDB = greenlet(async (limit) => {
   var db;
   var request = indexedDB.open("MyTestDatabase");
   request.onerror = function(event) {
     alert("Why didn't you allow my web app to use IndexedDB?!");
   };
   request.onsuccess = function(event) {
     console.log("Success!");
     // Never called
   };
});

Let me know if there's any other information I can provide that might be useful here.

Generator functions

Is it possible to support generator functions in addition to async functions?

Firefox doesn't support comments in provided async function

More just an observation than a bug; Firefox doesn't appear to handle comments inside of the passed async function. This appears to be because it puts all the code onto one line which causes the parsing to break via a SyntaxError i.e.:

SyntaxError: missing } after function body
data:$$=async%20(data,%20stringify)%20=>%20{//%20Commentif%20(stringify)%20{data%20=%20JSON.parse(da:1:416
note: { opened at line 1, column 30

in short, this will cause an error:

	const exampleAsyncFunc = async (data) => {
            // A wild comment appears
 	    return data
	}

but this does not:

	const exampleAsyncFunc = async (data) => {
 	    return data
	}

Uncaught SyntaxError: Unexpected identifier in Chrome 69

Greenlet works great ๐Ÿ‘Œ

But it throws a worrying syntax error every time I call my greenlet function. I'm probably holding something wrong, but can't figure out what.

screenshot 2018-10-17 at 16 53 31

This goes away if I import but never call greenlet().

Here's how I'm using it:

import greenlet from 'greenlet';

function doTheThing(data) {
  greenlet(storeData(data));
}

async function storeData(data) {
  return BaseAPIManager.makeAPICall({
    endPoint: "...",
    data
  }).catch(err => save_data_to_localstorage(data))
}

Any idea what I could be doing wrong?

name suggestions

like you said. the name isn't that meaningful ;)

these are also available:
threadit
threadd
workk
workthread

where my favourite is threadit

Synchronous functions broken in TypeScript

Trying to compile

import greenlet from 'greenlet';

const f = greenlet((foo: string) => foo);

Results in the following error

Argument of type '(foo: string) => string' is not assignable to parameter of type 'AsyncFunction<{}>'

I'd be happy to supply a PR with a fix!

TypeScript version: 3.3.4
greenlet version: 1.1.0

Question about caveat

Regarding the caveat (the function you pass cannot rely on its surrounding scope, since it is executed in an isolated context), does it mean you can't import external modules to be used in the function? What if the external modules were imported inside the function via require?

Too many levels of indentation

The deepest line has 8 levels of indentation, which is honestly way too many. I'd suggest building the web worker code string in a variable, then simply do new WebWorker(URL.createObjectURL(new Blob([code]))). Then you'd be down to 4 levels.

_ref not defined in Create react app.

We are planning to use greenlet to defer expensive computations to web worker in CRA project ( along with Type script ). To test it out I was playing around with a small example.

greenlet-helpers.ts

import greenlet from 'greenlet';

export const greenletRead = greenlet(async msg => {
  return msg;
});

utils.ts

import { greenletRead } from './greenlet-helpers';

try {
    const resp = await greenletRead('Hey from greenlet');
    console.log(resp);
} catch (e) {
     console.log('Exception', e);
 }

Running into _ref is not defined when ever this gets invoked. It would be great if I can get any pointers to get this started.

P.S. I also tried moving the code snippet from greenlet-helpers to uitils and still running into the same issue.

utils.ts

import greenlet from 'greenlet';

try {
   const greenletRead = greenlet(async msg => {
       return msg;
    });

    const resp = await greenletRead('Hey from greenlet');
    console.log(resp);
} catch (e) {
     console.log('Exception', e);
 }

ReferenceError: _isomorphicFetch2 is not defined

I got this error when using my fetchJSON function. This is my code:

const fetchFunc = (url, isServer = false) => {
  return fetch(url).then(res => {
    if (res.status !== 200) {
      throw new Error('Not 200 status')
    }
    return res.json()
  })
}

const fetchJSON = typeof window !== 'undefined' && window.Worker
  ? greenlet(fetchFunc)
  : fetchFunc

Update:
With async/await code the error become 'ReferenceError: _ref is not defined'

const fetchFunc = async (url, isServer = false) => {
  const res = await fetch(url)
  if (res.status !== 200) {
    throw new Error('Not 200 status')
  }
  return await res.json()
}

const fetchJSON = typeof window !== 'undefined' && window.Worker
  ? greenlet(fetchFunc)
  : fetchFunc

Without greenlet, my code works well
const fetchJSON = fetchFunc

Any helps would be appreciated! Thank you!

Releasing thread and URL reference when the workerized function is not needed

Great library, very useful stuff and absolutely love the size. :)

I've only recently started learning about Web Workers and took a look at the source code. So apologies in advance if I am wrong ;). 2 things caught my eyes:

const workerURL = URL.createObjectURL(new Blob([script]));
// Create an "inline" worker (1:1 at definition time)
const worker = new Worker(workerURL);

So if we do something like the following snippet (taken from the README), it seems that each new function instantiated via greenlet(...) will reserve a new thread and a new URL reference.

import greenlet from 'greenlet'

let getName = greenlet( async username => {
    let url = `https://api.github.com/users/${username}`
    let res = await fetch(url)
    let profile = await res.json()
    return profile.name
})

console.log(await getName('developit'))

So, if there is a case wherein I don't need to use getName after a certain point in my code, those resources are still trapped. They may be very less in size to be of a practical concern, but I am not sure about it and would love if anyone can comment on that.

However, if the output function getName comes with a dispose method which releases those references, it could be useful. WDYT? Something like:

getName.dispose() // Release references 

Internally, it could call:

window.URL.revokeObjectURL(workerURL);
worker.terminate();

Post dispose, getName can itself become undefined so it's not callable. Or can throw a more informative error: The function is disposed/discarded due to .dispose() call..

Is there a downside to this approach if the contributors already considered any similar approach?

Small inputs?

Greenlet is most effective when the work being done has relatively small inputs/outputs.

Mind giving some clarity on this? Are we speaking small data sets?

Worker pool?

Creating a new worker for every invocation can be rather slow when dispatching small tasks. How about adding a pool of workers?

Possible to use postMessage inside workerized function?

I was wondering if there was a way to use postMessage inside a function that gets created by greenlet.

My use case for this would some kind of file upload function that should let the main thread know of the current progress of the upload.

If this isn't possible with this, I guess I would have to go the old fashioned way, and create a fileupload.js and import that manually.

Can't find variable: Promise

[Vue warn]: Error in mounted hook: "ReferenceError: Can't find variable: Promise"
I got this error when runing Safari 5.1.7,What should I do?

Worker is not defined

Got this error. I did a regular npm install and it cant seem to run.

ReferenceError: Worker is not defined
    at e (/Users/josh/Desktop/node-playground/node_modules/greenlet/dist/greenlet.js:1:87)
    at Object.<anonymous> (/Users/josh/Desktop/node-playground/examples/greenlet-multithread.js:3:11)
    at Module._compile (module.js:569:30)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:503:32)
    at tryModuleLoad (module.js:466:12)
    at Function.Module._load (module.js:458:3)
    at Module.require (module.js:513:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/Users/josh/Desktop/node-playground/server.js:4:1)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: `node server.js`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

__awaiter is not defined

I keep getting greenlet.m.js:1 Uncaught (in promise) ReferenceError: __awaiter is not defined. In our project. I've tried changing the complier option values:
"importHelpers": true,
"noEmitHelpers": true,

in our tsconfig file. Has anyone experienced this problem and fixed it?

I'm just trying this in one of our modules

const getPost = greenlet(async () => {
  const url = `https://jsonplaceholder.typicode.com/posts`;
  const res = await fetch(url);
  return await res.json();
});

console.log('func======', getPost());

Thanks
Anthony

Cannot find module 'greenlet'. ts(2307)

We're having a small typescript lib here that we want to use greenlet in. Unfortunately, greenlet does not get recognised.

Out tsconfig.json looks like this:

{
  "compilerOptions": {
    "target": "es2017",
    "noImplicitAny": false,
    "declaration": true,
    "moduleResolution": "node",
    "outDir": "./dist"
  }

...
}

Cannot use greenlet in node. TypeError: URL.createObjectURL is not a function

I am trying to use greenlet in an isomorphic application, where the code will be run in browser and server. I am struggling to use it in node, where I keep getting TypeError: URL.createObjectURL is not a function.

I have tried the following polyfill but no luck.

global.Worker = require('tiny-worker')
global.Blob = require('cross-blob')
global.URL = require('url-polyfill')

What should I do to run greenlet in node?

support module workers?

Currently the worker is always created in classic mode. Since firefox now supports module workers, it would be nice to have the option for it in greenlet. It would open up some useful functionality, like dynamic imports.

I can submit a PR if this sounds desirable.

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.