admin管理员组

文章数量:1323529

Consider the following strange behavior I noticed in one of my projects:

async function hello() {
  return arguments;
}

When TypeScript's pilation target is set to es3 or es5, the above file fails to pile with the following error:

error TS2522: The 'arguments' object cannot be referenced in an async function or method in ES3 and ES5. Consider using a standard function or method.

2   return arguments;
           ~~~~~~~~~

However, with a higher pilation target (I've tested es2017 and esnext) there is no error.


What is it about the arguments keyword that prevents it from being used in async functions when TypeScript's pilation target is set to es3 or es5?

A few notes:

  • When replicated in modern JavaScript, this function does not throw an exception
  • This behavior can only be replicated in async functions

My Hypothesis

I suspect that because Promise needs to be polyfilled in es3 and es5, the polyfill cannot support arguments because it is dependant on the function callee.

Further reading: ES5.1 Spec § 10.6 Arguments Object

Consider the following strange behavior I noticed in one of my projects:

async function hello() {
  return arguments;
}

When TypeScript's pilation target is set to es3 or es5, the above file fails to pile with the following error:

error TS2522: The 'arguments' object cannot be referenced in an async function or method in ES3 and ES5. Consider using a standard function or method.

2   return arguments;
           ~~~~~~~~~

However, with a higher pilation target (I've tested es2017 and esnext) there is no error.


What is it about the arguments keyword that prevents it from being used in async functions when TypeScript's pilation target is set to es3 or es5?

A few notes:

  • When replicated in modern JavaScript, this function does not throw an exception
  • This behavior can only be replicated in async functions

My Hypothesis

I suspect that because Promise needs to be polyfilled in es3 and es5, the polyfill cannot support arguments because it is dependant on the function callee.

Further reading: ES5.1 Spec § 10.6 Arguments Object

Share Improve this question asked Jun 27, 2019 at 20:19 brenbren 4,3443 gold badges30 silver badges44 bronze badges 4
  • 3 Your hypothesis sounds right. Remove return arguments than have a look at what the transpiler generates – Jonas Wilms Commented Jun 27, 2019 at 20:22
  • This has less to do with promises than rather with the generator continuations. I'm pretty sure you'll see the same error in a generator function. And to be honest, they should've been able to work around this, just like you can "work around" having no arguments or this in an arrow function. – Bergi Commented Jun 27, 2019 at 21:17
  • @bergi but whey should they? There is no use of arguments. – Jonas Wilms Commented Jun 27, 2019 at 21:21
  • @JonasWilms Just to follow the spec closely (which allows arguments) and not have users trip over error messages :-) – Bergi Commented Jun 27, 2019 at 21:32
Add a ment  | 

3 Answers 3

Reset to default 6

It's because Async functions get transpiled to a basic polyfilled implementation of generators; this implementation basically swaps out the body of your function to wrap it in another function, therefore any use of arguments will never access the original arguments of hello but instead of __generator

An example of this is below where hello which takes no arguments and generates the following in which the body of the function is wrapped in another function.

async function hello() {
    console.log(arguments);
} // bees.....


function hello() {
    return __awaiter(this, arguments, void 0, function () {
        return __generator(this, function (_a) {
            console.log(arguments);
            return [2 /*return*/];
        });
    });
}

to re-iterate, console.log(arguments) has moved from the context of hello to the context of __generator meaning it would never behave how you expect it would. If you're targetting modern browsers (non-IE) you can set your pile target to ES6+ in which case this restriction will be removed.

Just use the spread operator

hello(...args: any[])

now you have an array or the arguments that were passed in.

You are absolutely right.

I'd like to share another point of view on why that does not work:

JavaScript has task based concurrency, which means that the code is broken into small "chunks" (tasks) and one of them gets executed at a time. If you have something asynchronous that gets divided into multiple tasks, one to start the async action and one to work with the results when the async action is done. Other tasks can run in the meantime, thus allowing for concurrency.

Now the smallest possible chunk of execution was (before async) a function: A function always runs to pletion, you can't split up a function into multiple tasks.

With the introduction of the async keyword, there are async functions that don't run to pletion. They will be split up into smaller tasks (through awaits).

Now if you have to transpile an async function into a regular function you have one problem: You can't split up the tasks. Therefore you need multiple functions to represent one async function:

   async function(arg) { await a(); await b(); }
   // bees
   function(arg) { return a().then(function () { b(); }); }

Now as one can see that does not transpile exactly: While arg was the argument of the only async function, only the outer function has that arg. However that is usually not a problem, wether you access arg in the current scope or in an outer scope does not change the way things work (except you redeclare it).

However there is one thing that was changed, and which made up this answer: arguments. As we have two functions we do have two arguments objects. Now one could also mimic the arguments object, but then you would have to use other unsupported newer language features (getters / setters, Proxies). And to be honest: You should not use arguments and thus transpiling it is not worth the trouble.

本文标签: javascriptTypeScript arguments keyword in async functionsStack Overflow