API v40 slows down Apex generated data in Lightning Components

Given a super simple scenario loading a list of 2000 Strings via Apex controller. When I process the list in a Lightning Component, there is a huge difference in performance between API v39 and v40. Here is a simplified example:

MyDataController.cls:

 public with sharing class MyDataController {
    @AuraEnabled
    public static String[] getStringData() {
      String[] myDataList = new List<String>();
      for(Integer i = 0; i < 2000; i++) {
        myDataList.add('element_' + i);
      }
      return myDataList;
    }
  }

TestApplication.app:

<aura:application controller="MyDataController">
  <aura:attribute name="myDataList" type="String[]" />

  <aura:handler name="init" value="{!this}" action="{!c.onInit}" />

  <button onclick="{!c.onButtonClick}">Click</button>
</aura:application>

TestApplicationController.js:

({
    onInit: function(component, event, helper) {
        var action = component.get("c.getStringData");
        action.setCallback(this, function(response) {
            if (response.getState() === "SUCCESS") {
                component.set("v.myDataList", response.getReturnValue());
            }
        });

        $A.enqueueAction(action);
    },

    onButtonClick: function(component, event, helper) {
        var dataList = component.get("v.myDataList");
        var element;
        var i = 0;
        console.time("loop_time");
        for(i; i < 2000; i++) {
            element = dataList[i];
        }
        console.timeEnd("loop_time");
    }
})

So clicking the button measures the loop execution time, where I literally do nothing then accessing each element – no further logic, no events, no rendering occuring.

With API v39 this is super fast as expected (multiple clicks to validate time):
enter image description here

But when I change both classes to API v40 this gets dramatically slower:
enter image description here

How could this be?
Even if this would relate to LockerService this does not make sense since I only have a plain list with strings.

Interesting fact: when I create the same kind of list in the init-method with pure JavaScript and store it back in the attribute I don’t see a performance loss when clicking the button. Lightning Component Debug mode has been disabled all time.

Answer

Ok, this was interesting.

I was able to optimize a little… then a lot.

First optimization: I used a forEach loop. This took the time from about 700ms to 7-14ms.

Eg

onButtonClick: function(component, event, helper) {
    var dataList = component.get("v.myDataList");
    var element;
    console.time("loop_time");
    dataList.forEach(function(item){
        element = item;
    });
    console.timeEnd("loop_time");
}

EDIT My third attempt works. The second attempt was a red herring, as I had the wrong API version set while testing.

Essentially, it involves getting around the built in Locker Service getters and setters by storing the data as a serialised string.

So, this will fix it (albeit in an annoying, parse it yourself kind of way)

Apex Controller:

@AuraEnabled
public static String getStringData() {
  String[] myDataList = new List<String>();
  for(Integer i = 0; i < 2000; i++) {
    myDataList.add('element_' + i);
  }
  return JSON.serialize(myDataList);
}

Attribute list is stored as a single string:

<aura:attribute name="myDataString" type="String" default="" />

Javascript init:

onInit: function(component, event, helper) {
    var action = component.get("c.getStringData");
    action.setCallback(this, function(response) {
        if (response.getState() === "SUCCESS") {
             component.set("v.myDataString", response.getReturnValue());
        }
    });
    $A.enqueueAction(action);
},

Click handler:

 onButtonClick: function(component,event,helper) {
    var temp = component.get("v.myDataString");
    var dataList = JSON.parse(temp);
    var element;
    console.time("loop_time");
    dataList.forEach(function(item){
        element = item;
    });
    console.timeEnd("loop_time");
}

I have checked the API version and I can confirm that this blazing fast again!
However, of course it’s a pain. I guess you’d use it in situations where you have a large list.

Attribution
Source : Link , Question Author : Christian Menzinger , Answer Author : Caspar Harmer

Leave a Comment