developit / greenlet

🦎 Move an async function into its own thread.
https://npm.im/greenlet
4.67k stars 100 forks source link

Question about caveat #12

Closed rclai closed 6 years ago

rclai commented 6 years ago

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?

Kanaye commented 6 years ago

You're right, it also means you can't import or require modules. greenlet works by stingifying the passed function and then executing it inside a worker. This doesn't work with imported or required modules because of how webpack bundles modules.

An example of what happens: someFn.js:

// this file is "someFn.js" or you yould replace it with any module like "lodash" ...
export default function someFn() {
  // .... your logic here ...
}

index.js:

import greenlet from 'greenlet';
import someFn from './someFn';

const myFn = greenlet( async (someVar) => someFn(someVar));

myfn(42);

This will be compiled by webpack to something like (I've removed and reformatted some parts for readability):

(function(modules) {
  // webpack "wiring" code ...
})([
(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_greenlet__ = __webpack_require__(1);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__someFn__ = __webpack_require__(2);
const myFn = Object(
    __WEBPACK_IMPORTED_MODULE_0_greenlet__["a" /* default */])( 
        async (someVar) => Object(__WEBPACK_IMPORTED_MODULE_1__someFn__["a" /* default */]
    )(
    someVar
    )
);

myfn(42);

/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
  // greenlet code ...
}),(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = someFn;
// this file is "someFn.js" or you yould replace it with any module like "lodash" ...
function someFn() {
  // .... your logic here ...
}

/***/ })
/******/ ]);

When greenlet calls toString on the provided function it will only get "async (someVar) => Object(__WEBPACK_IMPORTED_MODULE_1__someFn__[\"a\" /* default */])(someVar) which will only result in errors when executed in the webworker because __WEBPACK_IMPORTED_MODULE_1__someFn__ is not defined there.

Similar when we change the index.js to use require within the async function:

import greenlet from 'greenlet';

const myFn = greenlet( async (someVar) => {
    return require('./someFn')(someVar);
});

myfn(42);

The only thing that changes is the chunk generated by the index.js it now looks like this:

// ... other webpack/module code
function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_greenlet__ = __webpack_require__(1);

const myFn = Object(__WEBPACK_IMPORTED_MODULE_0_greenlet__["a" /* default */])(
    async (someVar) => {
        return __webpack_require__(2)(someVar);
});

myfn(42);

/***/ }
// ... other webpack module code

Now the stringified function executed by the worker will look like: async (someVar) =>{ return __webpack_require__(2)(someVar); } which still will throw an error.

Someone could probably write a loader similar to the workerize-loader which applies webpack magic to modules in a way that they will work within workers. But in my opinion this isn't worth it and it's out of scope for greenlet. If you need webpacks magic you can just use workerize and it 's loader. But that's just my opinion.

I hope I've been clear on why at least webpack imports will not work. You could probably use native import statements or something like systemjs but I haven tried neither of those. I've only used greenlet for self written, pure functions.

rclai commented 6 years ago

Thank you for the thorough answer.