Giter VIP home page Giter VIP logo

async-to-gen's Introduction

async-to-gen

npm Build Status

Turn your JavaScript with async functions into ES6 generators so they can be used in modern browsers or in node.js (v0.12 or newer).

Async functions are an exciting new proposed addition to JavaScript. The v8 team is hard at work getting it right, which means it could appear in future versions of node.js. However if you're impatient like me, then you probably just can't wait to get rid of your promise triangles and callback hell.

You can use Babel to accomplish this, but async-to-gen is a faster, simpler, zero-configuration alternative with minimal dependencies for super-fast npm install and transform time.

Also, async-to-gen provides support for async generators which return Async Iterators, a great syntax and primitive for producing and operating on streams of data.

Get Started!

Use the command line:

npm install --global async-to-gen
async-to-gen --help
async-to-gen input.js > output.js

# source maps!
async-to-gen input.js --sourcemaps inline > output.js

Or the JavaScript API:

npm install async-to-gen
var asyncToGen = require('async-to-gen');
var fs = require('fs');

var input = fs.readFileSync('input.js', 'utf8');
var output = asyncToGen(input).toString();
fs.writeFileSync('output.js', output);

// source maps!
var map = asyncToGen(input).generateMap();
fs.writeFileSync('output.js.map', JSON.stringify(output));

Use async-node

Wherever you use node you can substitute async-node and have a super fast async functions aware evaluator or REPL.

$ async-node
> async function answer() {
... return await 42
... }
undefined
> promise = answer()
Promise { <pending> }
> promise.then(console.log)
Promise { <pending> }
42

Use the require hook

Using the require hook allows you to automatically compile files on the fly when requiring in node, useful during development:

require('async-to-gen/register')
require('./some-module-with-async-functions')

You can also provide options to the require hook:

// Disable inline source maps for use with development tools.
require('async-to-gen/register')({ sourceMaps: false })

Use options to define exactly which files to includes or excludes with regular expressions. All files are included by default except those found in the node_modules folder, which is excluded by default. Pass excludes: null to not exclude any files.

require('async-to-gen/register')({ includes: /\/custom_path\// })

The require hook also offers the ability to set options and revert the require hook:

const asyncHook = require('async-to-gen/register')
// Similar to example above.
asyncHook.setOptions({ includes: /\/custom_path\// })
// Reverts the require hook.
asyncHook.revert()

Don't use the require hook in packages distributed on NPM

As always, don't forget to use async-to-gen to compile files before distributing your code on npm, as using the require hook affects the whole runtime and not just your module and may hurt the runtime performance of code that includes it.

Use in Build Systems:

Rollup: rollup-plugin-async

Common Usage

Async I/O

Async functions are great for writing asynchronous code that looks synchronous, and that's perfect for writing code that needs to perform async I/O operations.

One of the original motivations for Node.js was non-blocking I/O, perfect! However its core libraries do not yet support Promises, nor do many popular libraries written for Node 😭.

Do not fret, we can fix this with promisify-node!

const promisify = require('promisify-node');
const fs = promisify('fs');

async function emojify(filePath) {
  const contents = await fs.readFile(filePath, 'utf8')
  const edited = contents.replace(/:\)/g, '😉')
  await fs.writeFile(filePath, edited, 'utf8')
}

Mocha

Writing tests in mocha? Async functions are super handy for testing any code using promises and already work out of the box! To enable async functions in mocha include the require hook when you run your tests:

mocha --require async-to-gen/register test.js

Then in your tests, use async functions in your it clauses:

describe('My Promising Module', () => {

  it('promises to give a value', async () => {
    expect(await myFunction('input')).to.equal('output')
  })

})

Testing your express app? Try supertest and async functions:

const express = require('express')
const request = require('supertest')

const app = express()
app.get('/foo', (req, res) => res.send('bar'))

describe('My express app', () => {

  it('loads foo', async () => {
    const response = await request(app).get('/foo')
    expect(response.body).to.equal('bar')
  })

})

Jest

Writing tests in Jest? Use the scriptPreprocessor entry in your jest configuration in package.json:

{
  "name": "my-project",
  "jest": {
    "transform": {
      "^.+\\.js$": "async-to-gen"
    }
  },
  "devDependencies": {
    "async-to-gen": "*"
  }
}

Scripts

Have interactive scripts that require lots of input from the user? Async functions make writing those much easier! Check out interactive-script.

npm install interactive-script

Then write your script:

const interactive = require('interactive-script')
interactive(async (say, ask) => {
  say('What is your favorite number?')
  let num;
  do {
    num = Math.ceil(Math.random() * 100)
  } while (!await ask(`Is it ${num}? `, 'yN'))
  say(`Great, I think ${num} is a fine number.`)
})

And run it with async-node:

async-node myScript.js

Streams

Streaming data can be a challenging thing to get right. While Observables have provided a great library for streamed data, Async Generators provides language-level support for this concept!

Consider subscribing to a web socket within an program using async functions:

async function* stockTickerInEuro(symbol) {
  var socket = await openWebSocket('ws://stocks.com/' + symbol)
  try {
    for await (var usd of socket) {
      var euro = usd * await loadExchangeRateUSD2EUR()
      yield euro
    }
  } finally {
    closeWebSocket(socket)
  }
}

Then calling this function produces an Async Iterator (an Iterator of Promises) of stock ticker values.

var ticker = stockTickerInEuro('AAPL')
ticker.next().then(step => console.log(step.value))

Or use for-await-of loops within another async function:

async function bloombergTerminal() {
  for await (var price of stockTickerInEuro('AAPL')) {
    console.log(price)
  }
}

Dead-Simple Transforms

When async-to-gen transforms async functions, it makes as few edits as possible, and does not affect the location of lines in a file, leading to easier to understand stack traces when debugging.

It also includes a very small conversion function at the bottom of the file. How small? 204 chars for async functions and 533 chars for async generators.

Before:

async function foo() {
  return await x
}

After:

function foo() {return __async(function*(){
  return yield x
}())}

function __async(g){/* small helper function */}

Using with Babel

Don't bother using both! If you're already using Babel (maybe you need JSX, other proposed features, or are supporting older versions of Node) then you might be excited to hear that using babel-preset-es2017 in your .babelrc will provide support for async functions!

Babel is an amazing tool that you should consider using, however async-to-gen intentionally makes some different choices to provide a different set of trade-offs. Babel is general-purpose and supports a wider set of features but requires some configuration and more dependencies and those features may cost build performance or output code quality. async-to-gen can only do one thing, but that simplicity lends itself towards faster builds and cleaner output.

Ultimately, if you only need to add async function support you should try async-to-gen but if you need more features then you should use Babel.

async-to-gen's People

Contributors

chentsulin avatar cognitom avatar leebyron avatar seep avatar thymikee 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

async-to-gen's Issues

TypeError: Cannot read property 'type' of null

I got this error, don't know why yet (checking and will update when i know what caused it)

.../node_modules/async-to-gen/index.js:451
if (node && typeof node === 'object' && (node.type || node.length && node[0].type)) {
                                                                                  ^
TypeError: Cannot read property 'type' of null

Probably a good idea to check if node[0].type exists and throw if it doesn't 😬 with a possible cause (once i've found out what caused this) 💭

`referencesArgs: true` causes SyntaxError in strict mode

Hi @leebyron, first of all thanks for such an awesome library! This + node6 = a dream.

I've got an issue where referencing arguments in an async function is causing async-to-gen to create an argument called arguments, which is disallowed in strict mode. It causes the following to happen:

screen shot 2016-11-02 at 10 02 47

Here is the original bit of code at that point:

screen shot 2016-11-02 at 10 08 18

Is there some way to fix this? I was looking at the source and trying to understand why you need to explicitly pass arguments through - how come you can't use apply with the original args?

Arrow function with parenthesised object literal causes syntax error

First off - thanks for a brilliant library. I'm using it in conjunction with FinishedPromise and mocha to write tests that cannot be slow :-)

I've found a bug though. This code:

async () => ({})

gets transpiled to this:

() =>__async(function*(){ (return {a:1}}()))

which is not valid JavaScript. I believe it ought to be translated to this (but I'm not 100% sure)

() =>__async(function*(){ return {a:1}}())

I tried various things in leaveArrowFunction, but couldn't come up with something that worked without breaking the tests yet. Would be great to have a fix for this!

aasync-to-gen breks babel-register

I am using the zeit/micro script that requires async-to-gen, but now my runtime babel-register setup breaks. I filed a report there (#89), but I now believe it is because of this lib. Could you wrap your .js resolver AFTER the previously registered ones?

Wait, I have fixed it and will submit a pull request...

memory leak

Test case --

let out;

async function longLoop() {
  for (let i = 0; i < 1000000; i++) await undefined;
  out = 1;
}

longLoop().then(() => console.log(1 === out));

Steps to reproduce --

  1. Run the above code through async-to-gen
  2. Run the generated code using node and set an upper bound on memory

Actual output --

➜   node --max-old-space-size=10  async-leak-gen.js                                                                        
<--- Last few GCs --->

     194 ms: Mark-sweep 16.0 (44.0) -> 15.9 (44.0) MB, 46.2 / 0.0 ms [allocation failure] [GC in old space requested].
     242 ms: Mark-sweep 15.9 (44.0) -> 15.9 (44.0) MB, 48.6 / 0.0 ms [allocation failure] [GC in old space requested].
     291 ms: Mark-sweep 15.9 (44.0) -> 15.7 (28.0) MB, 48.7 / 0.0 ms [last resort gc].
     344 ms: Mark-sweep 15.7 (28.0) -> 15.7 (28.0) MB, 52.5 / 0.0 ms [last resort gc].


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0x170d0accfb51 <JS Object>
    2: NewPromiseCapability(aka NewPromiseCapability) [native promise.js:175] [pc=0x379a595496a3] (this=0x170d0ac04381 <undefined>,O=0x170d0acc3059 <JS Function Promise (SharedFunctionInfo 0x170d0ac74f51)>)
    3: then [native promise.js:~215] [pc=0x379a59554f01] (this=0x1c8d0c9c9839 <a Promise with map 0x3b1fbab1b9b9>,z=0x1c8d0c9ca109 <
JS Function s (SharedFunctionInfo 0x6e897d5d7c1)>,A=0x1c8d...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
 1: node::Abort() [node]
 2: 0x10a08dc [node]
 3: v8::Utils::ReportApiFailure(char const*, char const*) [node]
 4: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [node]
 5: v8::internal::Factory::NewOneByteInternalizedString(v8::internal::Vector<unsigned char const>, unsigned int) [node]
 6: v8::internal::AstRawStringInternalizationKey::AsHandle(v8::internal::Isolate*) [node]
 7: v8::internal::StringTable::LookupKey(v8::internal::Isolate*, v8::internal::HashTableKey*) [node]
 8: v8::internal::AstValueFactory::GetString(unsigned int, bool, v8::internal::Vector<unsigned char const>) [node]
 9: v8::internal::AstValueFactory::GetOneByteStringInternal(v8::internal::Vector<unsigned char const>) [node]
10: v8::internal::Scanner::CurrentSymbol(v8::internal::AstValueFactory*) [node]
11: v8::internal::ParserBase<v8::internal::ParserTraits>::ParseAndClassifyIdentifier(v8::internal::ExpressionClassifier<v8::internal
::ParserTraits>*, bool*) [node]
12: v8::internal::ParserBase<v8::internal::ParserTraits>::ParsePrimaryExpression(v8::internal::ExpressionClassifier<v8::internal::Pa
rserTraits>*, bool*) [node]
13: v8::internal::ParserBase<v8::internal::ParserTraits>::ParseMemberExpression(v8::internal::ExpressionClassifier<v8::internal::Par
serTraits>*, bool*) [node]
14: v8::internal::ParserBase<v8::internal::ParserTraits>::ParseMemberWithNewPrefixesExpression(v8::internal::ExpressionClassifier<v8
::internal::ParserTraits>*, bool*) [node]
15: v8::internal::ParserBase<v8::internal::ParserTraits>::ParseLeftHandSideExpression(v8::internal::ExpressionClassifier<v8::interna
l::ParserTraits>*, bool*) [node]
16: v8::internal::ParserBase<v8::internal::ParserTraits>::ParsePostfixExpression(v8::internal::ExpressionClassifier<v8::internal::Pa
... <snip>

Expected output --
Running non async-to-gen version with ToT v8

➜   ./out.gn/x64.release/d8 ~/code/scratch/async-leak.js --max-old-space-size 10
true

Invalid async lambda generation

When using require('async-to-gen/register');
Calling an async lambda as async () => {} works
but calling async() => {} will cause the new method to fail compilation.

Support more extensions in register hook

I imagine many users may wish to use this with .jsx files, and possibly also with .es6 or similar files.

Of course, they can always do this with a method other than the register hook. But it might be surprising that it won't work with jsx files.

It might also be worth documenting that in order for jsx to be processed, you would need to do something like this:

require('async-to-gen/register')
require('babel/register')

with async-to-gen coming first. (see also #11)

SyntaxError: Unexpected identifier yield

This snippet works on Chrome 54 with native async/await

async function test() {
    return !!await console.log('works');
}

test();

With async-to-gen it leads to parse error. Simple workaround is to write return !!(await ....) instead.

Support for super keyword in classes

This fails because of the wrapper function:

class Base  {
  async foo() { }
}

class Something extends Base {
    async foo() {
        return await super.foo();
    }
}

Is there a way to remove the require hook?

From the README:

require('async-to-gen/register')
require('./some-module-with-async-functions')

What if only need the hook for one file? Is there a way to remove the hook after it's done being useful? Something like:

var undo = require('async-to-gen/register')
require('./some-module-with-async-functions') // works
undo()
require('./another-module-with-async-functions') // breaks

async-to-gen conflicts with babel

I believe it is in everybody's best interest to join these efforts with the babel plugin. Their ecosystem is widely used and this module only makes problems for babel users.

If anybody wants to just run babel's plugin-async-to-generator, they can do so. If anybody has a problem with getting it to work, contact the authors about a documentation problem. Please don't go rogue on us and start making conflicting modules that don't play nice with require.extensions philosophy.
#11 and #12 are related to this.

Unexpected yield from async function with for await statement

Here is code I have:

class Dispatcher {
  async dispatch(message) {
    this.mailbox.enqueue(message);

    if (!this.isDispatching) {
      this.isDispatching = true;

      for await (const message of this.mailbox) {
        for (const task of this.tasks) task(message);
      }

      this.isDispatching = false;
    }
  }
}

After using async-to-gen Dispatcher.js > D.js I'm seeing this:

class Dispatcher {
  dispatch(message) {return __async(function*(){
    this.mailbox.enqueue(message);

    if (!this.isDispatching) {
      this.isDispatching = true;

      for (let $await1 of this.mailbox) {const message=yield{__await:$await1};
        for (const task of this.queue) task(message);
      }

      this.isDispatching = false;
    }
  }.call(this))}
}

And there is a problem here:

const message=yield{__await:$await1};

A message becomes an object with the actual value inside which breaks consequent logic.

Is it a bug or something wrong with my code? It works with babel-node and babel-preset-stage-3.

async-to-gen compiles the tree

I think async-to-gen would be quite a bit more useful if it was more like async-node, where if you do async-to-gen index.js --out build it'll walk the dependencies and write them out.

If that works for you, I think the easiest way would be to create a require.extensions function that writes out the transformed files.

Better output when parser fails

Right now if you have a syntax error, babylon fails but doesn't actually tell you where the issue is - the stack trace just points to babylon itself:

screen shot 2016-11-20 at 19 35 40

Is there any way to get better output from this, or am I missing something?

Support for static methods

This fails to compile:

class Bar extends Object {
    static async create(props) {
        return 'foo';
    }
};

Node error

/Users/ioksanen/git/mas/test.js:3
     async create(props) {return __async(function*(){
           ^^^^^^
SyntaxError: Unexpected identifier
    at Object.exports.runInThisContext (vm.js:76:16)

Problem seems to be static keyword.

Will the register hook work with Babel's register hook?

Reading register.js, it looks like prev is not called for matching files: https://github.com/leebyron/async-to-gen/blob/master/register.js#L9. I imagine this would prevent other register hooks, such as Babel's, from working in conjunction with async-to-gen.

It isn't entirely clear to me how this should be handled. Perhaps simply raising an error if there is a preexisting require.extensions['.js'] and encouraging the user to load this before any other registers would work best.

Indlude a specific dir inside node_modules

I have a small library written with async-to-gen. At this point, I had to hack a bit like this:

// works
require('async-to-gen/register')({
  excludes: /\/node_modules\/(?!my-library\/)/
})

On the other hand, async-to-gen ignores node_modules by default, so the code below doesn't work:

// doesn't work
require('async-to-gen/register')

Is there any other good ways to use async-to-gen inside a library?

Or..., can we change this line if (isIncluded && !isExcluded) to if (isIncluded || !isExcluded)? Then we could simply write like this, I think:

// hope to work
require('async-to-gen/register')({
  includes: /\/node_modules\/my-module\//
})

Related to #31

BTW, I'm enjoying async/await. Thank you for your cool library 😄

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.