Bluebird: Bluebird.coroutine() is sometimes synchronous

Created on 26 Jul 2016  路  4Comments  路  Source: petkaantonov/bluebird

This is with latest Bluebird (3.4.1) and Babel (6.11.4).

Many Babel users are using Bluebird.coroutine() in combination with transform-async-to-module-method to have Bluebird shim async/await.

Unfortunately, it's not a perfect shim. The following code:

async function foo() {
  await syncFn();
  console.log(3);
}
async function syncFn() {
  return;
}
console.log(1);
foo();
console.log(2);

prints 1 3 2 with Bluebird.coroutine() and 1 2 3 with the default transform-async-to-generator method.

I've created this Phabricator ticket to track but it appears the issue lies here.

The above code transpiles to this, which I have cleaned up for readability:

var Bluebird = require('bluebird');

function foo() {
  return Bluebird.coroutine(function* () {
    yield syncFn();
    console.log(3);
  })();
}

function syncFn() {
  return Bluebird.coroutine(function* () {
    return;
  })();
}

console.log(1);
foo();
console.log(2);

This is easily runnable in any Node runtime that supports generators:

$ node syncCoroutines.js
1
3
2

Is this intended behavior?

bug

All 4 comments

Looks like a bug.

Minimal test case:

var Bluebird = require('bluebird');
var foo = Bluebird.coroutine(function* () {
    yield Bluebird.resolve();
    console.log(3);
})
console.log(1);
foo();
console.log(2);

Basically, "yield" will synchronously inspect the promise and continue execution immediately if its already resolved. Since no handlers are added to the promise in this case, the async trampoline is completely avoided.

Here's a very hacky workaround for until this gets fixed:

var shimmer = require('shimmer');
var AltPromise = Promise.getNewLibraryCopy();

shimmer.wrap(Promise, 'coroutine', function(original) {
    return function(generatorFunction, options) {
        if (typeof generatorFunction === 'function') {
            var generatorFunctionOriginal = generatorFunction;
            generatorFunction = function() {
                var generator = generatorFunctionOriginal.apply(this, arguments);
                var next = generator.next;
                generator.next = function() {
                    var ret = next.apply(this, arguments);
                    if (ret.value instanceof Promise && !ret.value.isPending()) ret.value = AltPromise.resolve(ret.value);
                    return ret;
                };
                return generator;
            };
        }

        return original.call(this, generatorFunction, options);
    };
});

(the problem only occurs when the yielded promise is an instance of the main bluebird Promise constructor)

Amazing you guys fixed this so fast. Any idea when it's likely to get pushed to npm?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

srguiwiz picture srguiwiz  路  6Comments

julien-f picture julien-f  路  5Comments

wearhere picture wearhere  路  5Comments

rainabba picture rainabba  路  5Comments

icodeforlove picture icodeforlove  路  3Comments