question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Bluebird seems to not handle async/await properly

See original GitHub issue
  1. What version of bluebird is the issue happening on? 3.5.4

  2. What platform and version? (For example Node.js 0.12 or Google Chrome 32): Node v8 and Node v12

  3. Did this issue happen with earlier version of bluebird? Also tried with v3.5.0 and same issue is occuring

The following code illustrates the issue. The code maked as from the 3rd party library is intended execute a series of promises in order (fooLoader, barLoader, bazLoader) where barLoader should not execute until fooLoader has resolved.

const log = (message: string) => {
  // tslint:disable-next-line:no-console
  console.log(`${new Date().toISOString()}    ${message}`);
};

const sleep = (timeToSleepMs: number): Promise<void> => {
  return new Promise<void>(res => setTimeout(res, timeToSleepMs));
};

// this code is taken from a 3rd party library
function runInSequence<T, U>(
  collection: T[],
  callback: (item: T) => Promise<U>
): Promise<U[]> {
  const results: U[] = [];
  return collection
    .reduce((promise, item) => {
      return promise
        .then(() => {
          return callback(item);
        })
        .then(result => {
          results.push(result);
        });
    }, Promise.resolve())
    .then(() => {
      return results;
    });
}

const fooLoader = async () => {
  log("foo loader begin");
  await sleep(1000);
  log("foo loader complete");
};

const barLoader = async () => {
  log("bar loader begin");
  await sleep(2000);
  log("bar loader complete");
};

const bazLoader = async () => {
  log("baz loader begin");
  await sleep(3000);

  log("baz loader complete");
};

const runIt = (message: string) => {
  return (
    runInSequence([fooLoader, barLoader, bazLoader], loader => {
      const loaderResult = loader();
      return loaderResult instanceof Promise ? loaderResult : Promise.resolve();
    })
      .then(() => log(message))
      // tslint:disable-next-line: no-console
      .catch(err => console.error(err))
  );
};

const runWithoutBluebird = () => {
  log("running without Bluebird...");
  return runIt("without bluebird complete");
};

const runWithBluebird = () => {
  log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
  log("running with Bluebird...");
  const Promise = require("bluebird");
  Promise.config({ longStackTraces: true });
  global.Promise = Promise;

  return runIt("with bluebird complete");
};

runWithoutBluebird()
  .then(() => runWithBluebird())
  // tslint:disable-next-line: no-console
  .catch(err => console.error(err));

output:

2019-05-10T15:55:25.452Z    running without Bluebird...
2019-05-10T15:55:25.456Z    foo loader begin
2019-05-10T15:55:26.461Z    foo loader complete
2019-05-10T15:55:26.462Z    bar loader begin
2019-05-10T15:55:28.463Z    bar loader complete
2019-05-10T15:55:28.464Z    baz loader begin
2019-05-10T15:55:31.467Z    baz loader complete
2019-05-10T15:55:31.467Z    without bluebird complete
2019-05-10T15:55:31.467Z    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2019-05-10T15:55:31.467Z    running with Bluebird...
2019-05-10T15:55:31.495Z    foo loader begin
2019-05-10T15:55:31.495Z    bar loader begin
2019-05-10T15:55:31.496Z    baz loader begin
2019-05-10T15:55:31.496Z    with bluebird complete
2019-05-10T15:55:32.498Z    foo loader complete
2019-05-10T15:55:33.497Z    bar loader complete
2019-05-10T15:55:34.499Z    baz loader complete

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:9

github_iconTop GitHub Comments

1reaction
spioncommented, May 12, 2019

Actually - never mind, scratch that. I see the issue.

You are using instanceof Promise to check if the result is an instance of Promise. But async functions always return an instance of the native Promise, never of the patched up global. This is by spec.

So then, you’re returning a Promise.resolve() in the runInSequence function for every loader, because the returned Promise isn’t a bluebird one, which is what Promise refers to after the patch.

Modifying the runinsequence invocation to be:

runInSequence([fooLoader, barLoader, bazLoader], loader => loader());

solves the problem.

If you want to support non-async functions, I suggest instead of instanceof to use

if (p != null && typeof p.then !== 'function') return Promise.resolve() else return p;

Or better yet, just return Promise.resolve(p) will auto-convert to promise only when needed.

0reactions
mcalhouncommented, May 12, 2019

Ah…very interesting…and great catch! That code is actually from a 3rd party library (I just pulled it out to make the example standalone)…I will open a PR with them to make that change. Thanks @spion!

Read more comments on GitHub >

github_iconTop Results From Across the Web

ts-node not handling async/await properly with Bluebird #832
ts-node doesn't seem to be handling the resolution of promises correctly when using the Bluebird library globally.
Read more >
Get Bluebird Promise from async await functions
The constructor seems enclosed: Note that AsyncFunction is not a global object. It could be obtained by evaluating the following code. Object.
Read more >
petkaantonov/bluebird - Gitter
Hey, is there any way to tree shake the blue bird bundle to get the code which is needed for my usage. I...
Read more >
Gary Bernhardt on Twitter: "Oh, async/await always use native ...
It seems like I can get it if I use Bluebird with "longStackTraces" enabled. ... app's JS context is probably the only way...
Read more >
Promise.coroutine - Bluebird JS
Returns a function that can use yield to yield promises. Control is returned back to the generator when the yielded promise settles.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found