Moment.js vs Native Performance Issues

td;dr: avoid Moment.js date calculations in large and deep loops. See https://codepen.io/jerrylow/pen/MrjNXK

Couple months ago our team was wrapping up our project for testing. Up to this point we’ve been testing with test data or just small sample sets. Everything was working well and we were happy, confident and arrogant (jk). Once QA jumped in and started creating larger sample data the system started to crawl — by crawl I mean barely moving. We were seeing upwards of 12s response time from the server and 6s from the client. These were the slowest response time we were seeing, there were other parts of the site that was less than that but definitely not the milliseconds that we were accustomed to during development.

Our team started investigating, then my teammate found this article https://moshen.net/posts/moment_performance_on_node/ which discusses the performance issues with Moment.js — so we started testing. We started removing Moment.js calculations (isBetween, isBefore, add, subtract…) within large loops and the results were dramatic. We weren’t exactly seeing the 27 times increase as mentioned in the article but we were getting around 1~2s response time (vs 12s) from the backend and around the same on the front. Other optimizations beyond Moment.js were done to reduce the processing/response time.

For Demonstration Purpose

Since we couldn’t share the application or code snippets I’ve built this quick Codepen to demonstrate the “27 times” difference between Moment.js and native: https://codepen.io/jerrylow/pen/MrjNXK a screenshot:

Codepen to demonstrate performance difference between Moment.js vs native.

Now, when I wrote it and shared it I knew it would require more context as my colleague has pointed out in his response:

What I was trying to illustrate was the huge difference between using Moment.js vs native Date method. From the example the 21ms doesn’t seem like a lot (and probably affordable in many cases), but what was actually happening was we had some deeper level of nesting happening, for example:

const results = arr.map(a => {
if (moment(a.date).isBetween(startDate, endDate)) {
// More logical stuff
// More moment stuff.
}
});

Suddenly our milliseconds started adding up — exponentially. So you can see how the “27 times” performance improvement was so critical. In our case we weren’t doing the calculation on 25,000 rows of data, but each row takes more time than the simple example I’ve built. Our application was processing around 3000–5000 which was within our user requirements — so I don’t think it’s impossible for somebody to be working with something in the 10-20k data size.

So, Stop Using Moment.js?

No, that’s not actually what we did — Moment.js is still very useful. We’ve done a hybrid. We have a bunch of reusable native functions to do calculations such as add, subtract, same day/month/year and between. We use those functions as much as we can just to be performance conscious then use Moment.js for all formatting and some more controlled loops such as printing out the months of the year. If you are using Moment.js to do date calculations within large datasets it might be the root cause of performance issues try swapping out with some native javascript, but no need to drop it from your whole app.