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

Mixing native async and Node callback functions seems to cause unexpected behavior #1964

Closed
bbuck opened this issue May 7, 2024 · 4 comments

Comments

@bbuck
Copy link

bbuck commented May 7, 2024

It seems async functions that return nothing (implicitly resolves to undefined) pass on undefined as a value to the next function in line which I would not expect to happen. Returning undefined from a function is the default behavior of "returning nothing."

What version of async are you using?

3.2.4

Which environment did the issue occur in (Node/browser/Babel/Typescript version)
Node 18 (18.20.2 specifically)

What did you do? Please include a minimal reproducible case illustrating issue.

async.waterfall(
  [
    async () => {}, 
    (...args) => { 
      args.at(-1)(null, args); 
    },
  ],
  (error, result) => {
    console.log('error', error, '// result', result);
  },
);

// > error null // result [ undefined, [Function (anonymous)] ]

What did you expect to happen?

The result array to have a single element, the callback function.

What was the actual result?

The array contains two items, the undefined resolved from the native async function and the callback function passed to the Node-style async function.

@bbuck
Copy link
Author

bbuck commented May 7, 2024

Seems to affect 3.2.5 as well, just wanted to verify: https://runkit.com/bbuck/native-async-functions-in-async-waterfall-pass-undefined

@code-lixm
Copy link

I also encountered this problem. Using native Async will lose Callback and can only use Promise

@aearly
Copy link
Collaborator

aearly commented Sep 2, 2024

This is expected behavior. The first async function is implicitly returning undefined, so that is the return value passed to the next function. We can't detect the difference between an implicit or explicit return value if it is undefined. If you need it to be omitted entirely, use a callback function instead of an async function.

Or just use async/await for the whole flow, don't use waterfall. It's kind of obsolete now.

await (async fn () {})()
const result = await async.promisify((...args) => { 
  args.at(-1)(null, args); 
})

@aearly aearly closed this as completed Sep 2, 2024
@bbuck
Copy link
Author

bbuck commented Sep 2, 2024

The issue isn't necessarily that undefined is included in the final result it's that it's not expected. When you have the non-async version of the problematic code:

async.waterfall(
  [
    (cb) => { cb(null); }, 
    (...args) => { 
      args.at(-1)(null, args); 
    },
  ],
  (error, result) => {
    console.log('error', error, '// result', result);
  },
);

The "return value" passed to that callback function is, implicitly, undefined. Assuming you potentially look at arguments.length then you can tell nothing was provided but otherwise the value of that argument (and the rest) are all undefined.

The output of the above is error null // result [ [Function (anonymous)] ] which is what I would expect. However, by simply transforming the first function into an async function the entire problem resolves in a completely different way. I don't think the documentation calls this out clearly enough. The behavior is different enough that I think it should be clearly outline in the documentation and/or specific special cases be used for resolving to undefined (so probably for backwards compatibility reasons, just clearly called out in the documentation).

After all the entire reason I took the time to post this is because I rewrote a "node style" callback to a native async function and then had to debug why I continued to get "undefined is not a function" issues and I'm trying to prevent that from happening to anyone else.

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

No branches or pull requests

3 participants