Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promise support #956

Closed
rumkin opened this issue Nov 13, 2015 · 22 comments
Closed

Promise support #956

rumkin opened this issue Nov 13, 2015 · 22 comments
Labels

Comments

@rumkin
Copy link

rumkin commented Nov 13, 2015

Add promise support to allow mix different tasks:

async.waterfall([
    function (done) {
        // Do something and call done()
        fs.readFile(filepath, done);
    },
    function(content) {
        // Do something and return Promise
        return mongoose.models.file.create({
            name: filepath,
            content: content
        });
    }
], (err) => {
    if (err) {
        console.error(err);
    }
});
@aearly
Copy link
Collaborator

aearly commented Nov 13, 2015

asyncify will take a synchronous function that returns a Promise and call a callback on its resolved/rejected handlers:

async.waterfall([
    function (done) {
        // Do something and call done()
        fs.readFile(filepath, done);
    },
    async.asyncify(function(content) {
        // Do something and return Promise
        return mongoose.models.file.create({
            name: filepath,
            content: content
        });
    })
], (err, model) => {
    if (err) {
        console.error(err);
    }
});

But that feature isn't documented...

@aearly aearly added the docs label Nov 13, 2015
@aearly aearly added the feature label Jan 2, 2016
@aearly
Copy link
Collaborator

aearly commented Jan 2, 2016

I also had another thought -- should we automatically asyncify functions that return promises (or handle them appropriately?). What should async do when passed an ES-whatever async function (that implicitly returns a promise)?

e.g.

async.waterfall([
    function (done) {
        // Do something and call done()
        fs.readFile(filepath, done);
    },
    async function(content) {
        return await mongoose.models.file.create({
            name: filepath,
            content: content
        });
    }
], (err, model) => {
    if (err) {
        console.error(err);
    }
});

@SEAPUNK
Copy link

SEAPUNK commented Jan 5, 2016

Personally, I think async should asyncify promises by default. There are some async functions that I write that I also have to feed into async.queue, but I don't want to write this:

import {queue, asyncify} from 'async'

const run = asyncify(async function () {
  await someStuff()
})

const q = async.queue(run)
q.push('asdf')

where I could be writing this

import {queue} from 'async'

async function run () {
  await someStuff()
}

const q = async.queue(run)
q.push('asdf')

aearly pushed a commit that referenced this issue Jan 7, 2016
@aearly aearly removed the docs label Jan 7, 2016
@aearly
Copy link
Collaborator

aearly commented Jan 7, 2016

Added the docs for asyncify. Going to keep this open for the automatic asyncify-ing behavior.

@Kikobeats
Copy link
Contributor

About this, I was experimenting with use the same codebase and using a Promise interface for methods that use callback as last parameter. check it !

async.waterfall([
  function (callback) {
    callback(null, 'one', 'two')
  },
  function (arg1, arg2, callback) {
    // arg1 now equals 'one' and arg2 now equals 'two'
    callback(null, 'three')
  },
  function (arg1, callback) {
    // arg1 now equals 'three'
    callback(null, 'done')
  }
]).then(function (value) {
  console.log(value === 'done')
})

what do you think about it? I think that could be easy to adapt the library. See for example how works got library handle cb and promise.

@aearly
Copy link
Collaborator

aearly commented Jan 19, 2016

Very interesting. If Async properly handled functions that return promises, then it could also accept promisify-ed Async functions pretty easily. This would work:

async.parallel([
  async.waterfall([
    asyncFn1,
    function (result1, next) {
      //...
    }
    asyncFn2,
    //...
  ]), // no callback!
  async.each(arr, function (item, cb) {
    //...
  })
  otherAsyncFn
], done)

It would be much easier to combine Async functions.

The problem today is that callbacks are optional. Leaving off the last argument still executes the task. I would love to make is so Async functions are automatically curried -- if you leave off the callback, it partially applies the function, and all you need to do is call it with a callback. But seeing as many methods have optional parameters, and we're moving towards optional callbacks across the board, you cant really do it. Our methods are variadic, and you cant curry variadic functions.

However, if leaving off the final callback off a method has it return a Promise, and if Async is set up to handle functions that return promises, is an interesting combo. One major issue is then Async would have to integrate with a promise library -- which one? Do we rely on global.Promise, forcing older engines to polyfill, or do we use something like Bluebird? Also, Async is kind of a statement against promises -- higher-order functions for callback-accepting functions, rather than adopting the Futures paradigm. I'm all for interoperability with Promises, but I think having Async return promises is a bit outside its design philosophy.

@Kikobeats
Copy link
Contributor

@aearly

I feel that Promises is a workflow align with the last node versions (>=4). In this versions Promises are available, so, my vision is use Promise workflow when the global environment have Promises.

It's possible add a tiny polyfill (check pinkie-promise) but in my opinion don't have sense provide a polyfill. Better force the user upgrade the node version for use the last node features. Really, check (got 6 PR)[https://github.com/sindresorhus/got/pull/140]. Dependencies are not welcome in very tiny project and are used in everywhere.

Maybe this feature could be nice to be included after async modularization, that the codebase will be more easy to adapt.

But definetly, align async with promises is a totally must!

@rumkin
Copy link
Author

rumkin commented Jan 19, 2016

@Kikobeats 👍

Promises are becoming the first class async functions. So it's hard to imagine the must-heave library for asynchrony without full support of them.

I think it could be implemented without polyfill but with feature detection: allow if there is a global Promise object with resolve method.

@martinheidegger
Copy link

@aearly bluebird adds browser compatibility to the list of it polyfilling. I think it might be appropriate to use it.

@rumkin
Copy link
Author

rumkin commented Jan 19, 2016

@martinheidegger Bluebird is too big for this. It will cast 76 Kb (minified) for simple support of promise methods.

@Kikobeats
Copy link
Contributor

Again, use callback or promises interface in your backend workflow is a natural process based in your node version.

  • If you have a more time backend (2 years or more, maybe), you use use 0.10 or 0.12 version, so, your code is written in callback style.
  • If you backend have less than ~6 months and you use >=4 node version probably you use a Promise style.

But In any case you not have a mixed style.

So is not necessary add a dependency for support Promises; If async supports promises one day in the future, you can use it if your node version supports Promises.

Extreme case: I'm using a a older node version but my backend is written using promises style. Then you have have defined a Promises object as global or define one before use async. Whatever you want. Simply.

Same behavior for the frontend side. Not dependency is necessary.

@aearly
Copy link
Collaborator

aearly commented Jan 19, 2016

Hmmn, it seems the same problems Promises had 4-5 years ago still apply. If everyone was using node 4 or later, and modern browsers with ES6 support, I we could easily utilize promises internally where applicable.

The problem is we leave our node 0.12 and older browser users in the dust, requiring them to polyfill. Which polyfill do they use? We wouldn't want to bundle one, even pinkie-promise has weight to it. Also, people who are using promises outside of the standard ES6 Promise will want to use bluebird or q etc., whatever it is that they're using. I don't want to require the use of a polyfill -- npm i -S async should be all people need to do.

Async has also been somewhat of a reaction against Promises -- an alternative way to manage asynchronous code. To start using Promises seems like a step back from that. Properly managed callbacks can be just as powerful as promises (and in many cases, faster, since Promises have a built-in event-loop deferral).

I'm all for interop with promises, if a function is passed a Promise or other "thennable" than we should definitely take advantage of it. But I think we should avoid creating and/or returning Promises internally, just due to the fact ES6 support across platforms still has a way to go.

@megawac
Copy link
Collaborator

megawac commented Jan 19, 2016

Agreed, promises aren't cheap to create or process. I'd rather see something like this as an opt in extension. Promises are great but you won't always want them

@puzrin
Copy link

puzrin commented Jan 21, 2016

IMHO, after migrating a lot of node's code to promises + co + yield, i did not found direct replacement only for .eachLimit(). Probably, the list of useful cases is much less than looks at first glance, and can be handled with separate package.

@megawac
Copy link
Collaborator

megawac commented Jan 21, 2016

I like @Kikobeats' little extension, it may make sense to recommend it in the readme (/cc @aearly).

I would be strongly against official support of promises in async for reasons brought up by myself and other users.

@aearly
Copy link
Collaborator

aearly commented Jan 21, 2016

@megawac What about the case where a function returns a promise? e.g. @SEAPUNK 's example.

@Kikobeats
Copy link
Contributor

@megawac I Added support for callback style,tests and browser build in promise-async. Then if anybody want to use Promise, I think that is ready 👍

@megawac
Copy link
Collaborator

megawac commented Jan 24, 2016

@aearly, that'd be fine I suppose (so =) but I'd rather leave it in plugin
land personally

On Fri, Jan 22, 2016 at 2:17 PM, Kiko Beats notifications@github.com
wrote:

@megawac https://github.com/megawac I Added support for callback style
and tests for promise-async
https://github.com/Kikobeats/promise-async#promise-async. Then if
anybody want to use Promise, I think that is ready [image: 👍]


Reply to this email directly or view it on GitHub
#956 (comment).

@eladnava
Copy link

Here's another package that promisifies all the available async methods:
https://github.com/eladnava/koa-async

@nojvek
Copy link

nojvek commented Dec 17, 2016

I think just like there is async.asyncify there could be a async.promisify function.

Then I can just await async.promisify(async.mapLimit(x,10, mapper))

@megawac
Copy link
Collaborator

megawac commented Dec 17, 2016 via email

@tsuz
Copy link

tsuz commented Dec 30, 2016

I'm using bluebird module's promisifyAll method to convert callbacks into promises so that async's methods become:

// adds '<original-method>Async' to class 
Promise.promisifyAll(async);

function somePromise() {
    return async.parallelAsync([
        function(cb) {
            setTimeout(function(){
                cb(new Error('err'), 'foo')
            }, 1000);
        },
        function(cb) {
            setTimeout(function(){
                cb(null, 'bar')
            }, 1000);
        }
    ]);
}

somePromise().then(function(result){
    console.log('result',result);
}).catch(function(err){
    console.log('err',err);
});

JSFiddle Example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants