  Asynchrony has become an inherent element of JavaScript, as an effort to
improve the scalability and performance of modern web applications. To this
end, JavaScript provides programmers with a wide range of constructs and
features for developing code that performs asynchronous computations, including
but not limited to timers, promises, and non-blocking I/O.
  However, the data flow imposed by asynchrony is implicit, and not always
well-understood by the developers who introduce many asynchrony-related bugs to
their programs. Worse, there are few tools and techniques available for
analyzing and reasoning about such asynchronous applications. In this work, we
address this issue by designing and implementing one of the first static
analysis schemes capable of dealing with almost all the asynchronous primitives
of JavaScript up to the 7th edition of the ECMAScript specification.
  Specifically, we introduce the callback graph, a representation for capturing
data flow between asynchronous code. We exploit the callback graph for
designing a more precise analysis that respects the execution order between
different asynchronous functions. We parameterize our analysis with one novel
context-sensitivity flavor, and we end up with multiple analysis variations for
building callback graph.
  We performed a number of experiments on a set of hand-written and real-world
JavaScript programs. Our results show that our analysis can be applied to
medium-sized programs achieving 79% precision on average. The findings further
suggest that analysis sensitivity is beneficial for the vast majority of the
benchmarks. Specifically, it is able to improve precision by up to 28.5%, while
it achieves an 88% precision on average without highly sacrificing performance.
