[SalesForce] Upload CSV file in lightning component,but I have to click the button twice to insert the data

I need to up load CSV file to create new quotelineItem records in lightning component.And I create a new action button link to my component in the quote page,after I upload my csv,I click the import button.but first time it didn't work,after I click any button second time,it works.I write many console.log,i find first time it stop before action.setCallback(this, function(response),and second time that function work,I don't know why.

controller

({
handleFilesChange: function(component, event, helper) {
    var fileName = 'No File Selected..';
    if (event.getSource().get("v.files").length > 0) {
        fileName = event.getSource().get("v.files")[0]['name'];

    }
    component.set("v.fileName", fileName);
},
CreateRecord: function (component, event, helper) {
    var fileInput = component.find("file").get("v.files");
    var file = fileInput[0];
    //alert(file);
    if (file) {
    console.log("File");
    var reader = new FileReader();
    reader.readAsText(file, "UTF-8");
    reader.onload = function (evt) {
        console.log("EVT FN");
        var csv = evt.target.result;
        console.log('@@@ csv file contains'+ csv);
        var result = helper.CSV2JSON(component,csv);
        console.log('@@@ result = ' + result);
        //console.log('@@@ Result = '+JSON.parse(result));
        helper.CreateAccount(component,result);
    }
    reader.onerror = function (evt) {
        console.log("error reading file");
    }
}

}})

Helper

({
CSV2JSON: function (component,csv) {
console.log('@@@ Incoming csv = ' + csv);

    //var array = [];
    var arr = []; 

    arr =  csv.split('\n');;
    //console.log('@@@ Array  = '+array);
    console.log('@@@ arr = '+arr);
    arr.pop();
    var jsonObj = [];
    var headers = arr[0].split(',');
    for(var i = 1; i < arr.length; i++) {
        var data = arr[i].split(',');
        var obj = {};
        for(var j = 0; j < data.length; j++) {
            obj[headers[j].trim()] = data[j].trim();
            //console.log('@@@ obj headers = ' + obj[headers[j].trim()]);
        }
        jsonObj.push(obj);
    }
    var json = JSON.stringify(jsonObj);
    console.log('@@@ json = '+ json);
    return json;


},

CreateAccount : function (component,jsonstr){
 console.log('@@@ jsonstr' + jsonstr);
 var action = component.get("c.insertData"); 
    action.setParams({
            "strfromlex" : jsonstr
        });
    console.log("setParams has actived");
    action.setCallback(this, function(response) {
        console.log('@@@ SetCallback !');
        var state = response.getState();
        //alert(state);
        if (state === "SUCCESS") {              
        var resultsToast = $A.get("e.force:showToast");
            resultsToast.setParams({
                "title": "Success!",
                "type": "success",
                "message": "QuoteLineItems have imported."
            });
            resultsToast.fire();      
            var wasDismissed = $A.get("e.force:closeQuickAction");
            wasDismissed.fire();            
        }
        else if (state === "ERROR") {
            var errors = response.getError();
            if (errors) {
                if (errors[0] && errors[0].message) {
                    console.log("Error message: " + 
                             errors[0].message);
                }
            } else {
                console.log("Unknown error");
                 alert('Unknown');
            }
        }
    }); 
    $A.enqueueAction(action);}})

cmp

<aura:component controller="FileUploadController" implements="force:lightningQuickAction" >
<aura:attribute name="myRecordId" type="String" description="Record to which the files should be attached" />
<aura:attribute name="fileName" type="String" default="No File Selected.." />
<lightning:layoutItem padding="horizontal-small">
<div class="page-section page-header">
</div>
<lightning:input aura:id="file" 
                 onchange="{!c.handleFilesChange}" 
                 type="file" 
                 name="file" 
                 accept=".csv"
                 multiple="false"/>
<div style="margin-top:10px;" class="slds-text-body_middle slds-text-color_blue">{!v.fileName} </div>
<button style="margin-top:10px;" class="slds-button slds-button_brand" onclick="{!c.CreateRecord}">导入CSV文件<button>

Best Answer

The logic you have written is all correct the main thing creating trouble for is the FileReader.onload callback method, in this callback you are making the request to the server.

So put this inside the setTimeout like this, this should solve your problem for clicking the button twice to make the server side call inside a callback.

So replace your serverside method call from the callback like below with setTimeout:

window.setTimeout($A.getCallback(function(){
  helper.CreateAccount(component,result);
}), 10);