MatAtBread / fast-async

605 stars 21 forks source link

Promise is undefined #47

Closed z-vr closed 6 years ago

z-vr commented 6 years ago

How do I use this for IE9? I have in .babelrc:

      ["fast-async", {
        "compiler": {
          "promises": false
        },
        "useRuntimeModule": true
      }],

and even import nodent-runtime in my main file, but I keep seeing that Promise is undefined in IE. Could you please explain how to use it :(( it's so frustrating -- do I need to update webpack config to provide global Promise?

matAtWork commented 6 years ago

fast-async, unlike nodent (the compiler that actually does the transpiling) forces you to use Promises for compatibility reasons. Use a common Promises implementation and include it in your scripts. My recommendation (and used by the nodent-runtime) is https://github.com/bluejava/zousan

matAtWork commented 6 years ago

...or I think there is a Babel plugin that ensures Promise is defined globally if necessary

z-vr commented 6 years ago

but I saw that Zousan was in my code when bundled, so why is it there if it's not using it? Also what is the purpose of nodent-runtime then? screen shot 2017-11-15 at 15 37 24

I thought I could do

new webpack.ProvidePlugin({
      Promise: 'nodent-runtime/zousan',
    }),

but it results in an error that promise is not a function. Is there any way to reuse bundled zousan or not bundle it with nodent-runtime?

Sorry for noob questions, it's kind of confusing.

matAtWork commented 6 years ago

I'm not really familiar enough with WebPack to know how to provide a global value for Promise. My hack would be the line early in your initialisation code:

window.Promise = window.Promise || require('nodent-runtime/zousan')();

Note the brackets at the end - zousan.js provides a function (which can take a scheduler function), so you need to call it to get a reference to Promise. Alternatively, include the following lines in each file that requires support for Promises:

import getPromise from 'nodent-runtime/zousan' ;
const Promise = getPromise() ;

This doesn't pollute the global scope, but the convention seems to be a global polyfill for Promise is acceptable

z-vr commented 6 years ago

ok your vendored version does not have .all method, so I'll use actual Zousan. The thing I'm still not sure about, is whether promises: false should not work without Promises? and thanks for awesome lib. Would it be possible to require latest zousan in nodent-runtime so that dependencies could be dedupped -- or its full functionality is not required there? Although it's 1.65KB in nodent-runtime compared to 2.49KB in the zousan, when put together they take more than if only one was used in a project due to reusing the same package.

matAtWork commented 6 years ago

I checked the code, and promises: false should be supported, and then it uses the stripped down Zousan in nodent-runtime (which does some other things as well). You can see an example of the promise-free code generated [here](http://nodent.mailed.me.uk/#async%20function%20tellYouLater(sayWhat)%20%7B%0A%20%20%20%20%2F%2F%20Do%20something%20asynchronous%2C%20such%20as%20DB%20access%2C%20web%20access%2C%20etc.%0A%20%20%20%20return%20%22I%20said%3A%20%22%2BsayWhat%3B%0A%7D%0A%0Aasync%20function%20test()%20%7B%0A%20%20%20%20return%20tellYouLater(%22I'll%20tell%20you%20later%22)%20%3B%0A%7D%0A%0Aconsole.log(await%20test())%3B%0A~options~%7B%22mode%22%3A%22es5%22%2C%22promiseType%22%3A%22Zousan%22%2C%22noRuntime%22%3Afalse%2C%22es6target%22%3Afalse%2C%22wrapAwait%22%3Afalse%2C%22spec%22%3Afalse%7D) - there are no references to Promises in the generated code as they are internally supplied by the $asyncbind runtime function.

I'm reasonably convinced it works. From the fast-async installation (probably in your node_modules), try the the following:

npm test # This will install the babel-cli
cd tests
./node_modules/babel-cli/bin/babel.js ./test-input.js # Compile "test-input.js" and show the output

Now edit the "package.json" in the same directory (tests) to have the options you want. Mine look like:

  "babel": {
    "plugins": [
      [
        "..",
        {
      "compiler":{
            "promises": false
      },
      "useRuntimeModule": true
        }
      ]
    ]
  }

(Note: we use '..' for the plugin name here because we're testing the plugin in the parent directory - in your project it should say fast-async) Running the command line ./node_modules/babel-cli/bin/babel.js ./test-input.js generates code with no Promises (except the one explicitly in the original input file)

import _default from "nodent-runtime";
/* fast-async test file - it gets compiled by Babel using fast-async and other async-await implementations to measure performance */

function pause() {
    return new Promise(function ($return, $error) {
        setTimeout(function () {
            return $return(0);
        }, 0);
    });
}

function doNothing() {
    return function ($return, $error) {
        return $return();
    }.$asyncbind(this, true);
}

function test() {
    return function ($return, $error) {
        var t, j, i;

        t = Date.now();
        j = 0;
        return Function.$asyncbind.trampoline(this, $Loop_1_exit, $Loop_1_step, $error, true)($Loop_1);

        function $Loop_1() {
            if (j < 500) {
                i = 0;
                return Function.$asyncbind.trampoline(this, $Loop_3_exit, $Loop_3_step, $error, false)($Loop_3);

                function $Loop_3() {
                    if (i < 500) {
                        return doNothing().then(function ($await_5) {
                            return $Loop_3_step;
                        }.$asyncbind(this, $error), $error);
                    } else return [1];
                }

                function $Loop_3_step() {
                    i++;
                    return $Loop_3;
                }

                function $Loop_3_exit() {
                    return pause().then(function ($await_6) {
                        return $Loop_1_step;
                    }.$asyncbind(this, $error), $error);
                }
            } else return [1];
        }

        function $Loop_1_step() {
            j++;
            return $Loop_1;
        }

        function $Loop_1_exit() {
            return $return("Finished " + 500 * 500 + " async/awaits in " + (Date.now() - t) + "ms");
        }
    }.$asyncbind(this, true);
}

test().then(resolve, reject);

/* If you see this, then test-input.js was compiled by babel-cli. Things to check are that there's an import at the top for nodent-runtime, and lots of symbols starting with $ */
z-vr commented 6 years ago

@matAtWork thanks for the detailed instructions and ah OK you're right, the problem I had was because Webpack's dynamic loading requires Promise. On the other hand, would you consider implementing Promise.all in fast-async?

MatAtBread commented 6 years ago

I probably won't implement Promise.all in the runtime, since the purpose of the runtime is to provide the smallest, fastest Promise/A+ implementation as required by the spec for async and await. If you need more capable Promises, there are lots of libraries and native implementations that will also work with nodent.

If you really want that function, and no more, you can always use the implementation from https://github.com/bluejava/zousan/blob/master/src/zousan.js#L253

z-vr commented 6 years ago

OK sorry I got confused again, I thought fast-async was failing to compile my Promise.all([Promise.resolve(), Promise.reject()]) into nodent-runtime runnable code, but apparently it's not what it does!

matAtWork commented 6 years ago

Nope. It does compiles the async and await keywords, that cannot be implemented at run time. Promise is simply a global variable - it's not a language keyword - and is implemented at run time by a number of well-known libraries.

z-vr commented 6 years ago

@matAtWork promises:true // Use nodent's "Promises" mode. Set to false if your runtime environment does not support Promises (default: true) -- this made me think this plugin does some magik :P

matAtWork commented 6 years ago

Resolved in https://github.com/MatAtBread/fast-async/pull/48