プログラマ38の日記

主にプログラムメモです。

Salesforce:Lightning Promiseを使ってサーバコールを順番に処理する

Lightning Componentのクライアントサイドコントローラで業務ロジックを書く時に、サーバサイドコントローラのメソッドを順次呼び出したり、全部の処理が完了した後にメッセージ表示をしたりしたい時があります。

例えば、次のようなhelperとcontrollerを用意したとします。

//helper.js    
    invokeApex1 : function(arg, component, event, helper) {
        var serveraction = component.get('c.sampleCall1');
        serveraction.setParams({
            "value": arg
        });
        
        serveraction.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                //ロジック
            }
        });
        $A.enqueueAction(serveraction);
    }
    ,
    invokeApex2 : function(arg, component, event, helper) {
        var serveraction = component.get('c.sampleCall2');
        serveraction.setParams({
            "value": arg
        });
        
        serveraction.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                //ロジック
            }
        });
        $A.enqueueAction(serveraction);
    }    
//controller.js    
    handleClick : function(component, event, helper) {
        helper.invokeApex1('invoke01',component, event, helper);
        helper.invokeApex2('invoke02',component, event, helper);
		
        var toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            "title": "Success!",
            "message": "処理が完了しました。"
        });
        toastEvent.fire();
    }

 上記controllerの handleClick 実行時には、helper.invokeApex1 helper.invokeApex2 が完了していなくても、"処理が完了しました。" と表示されます。
これは、$A.enqueueAction が非同期のためです。

 

 上の処理をPromiseを使って書き直すと

//helper.js   
invokeApex1 : function(arg, component, event, helper) {
        return new Promise(function(resolve, reject){
            var action = component.get('c.sampleCall1');
            action.setParams({
                "value": arg
            });
            
            action.setCallback(this, function(response){
                var state = response.getState();
                if (state === "SUCCESS") {
                    //業務ロジック
                    resolve(response.getReturnValue());
                } else {
                    reject(new Error(response.getError()));
                }
            });
            $A.enqueueAction(action);            
        });
    }    
    ,
    invokeApex2 : function(arg, component, event, helper) {
        return new Promise(function(resolve, reject){
            var action = component.get('c.sampleCall2');
            action.setParams({
                "value": arg
            });
            
            action.setCallback(this, function(response){
                var state = response.getState();
                if (state === "SUCCESS") {
                    //業務ロジック
                    resolve(response.getReturnValue());
                } else {
                    reject(new Error(response.getError()));
                }
            });
            $A.enqueueAction(action);
            
        });
    }    
//controller.js   
    handleClick : function(component, event, helper) {
        
        const invokeApex1 = helper.invokeApex1('invoke01' ,component, event, helper);
        
        invokeApex1.then(function (response){
            //response は、invokeApex1のresolveの引数なので、invokeApex1の実行結果が使えます
            return helper.invokeApex2('invoke02' ,component, event, helper);
        }).then(function (response){
            //response は、invokeApex2のresolveの引数なので、invokeApex2の実行結果が使えます
            var toastEvent = $A.get("e.force:showToast");
            toastEvent.setParams({
                "title": "Success!",
                "message": "処理が完了しました。"
            });
            toastEvent.fire();                
        } , function (error){
            //エラー発生時の制御
        });
    }

 上記のように書き換えることで、非同期処理でも順番に実行することができます。

最後に

次のようにサーバサイドコントローラのメソッドをコールする場合、$A.enqueueAction(action); を一度に実行されます。そして、例えばSOQLの発行回数などはまとめて実行したメソッド全体で加算されていきます。

helper.invokeApex1('invoke01',component, event, helper); 
helper.invokeApex2('invoke02',component, event, helper);

特に、PromiseやsetCallback の中で次のactionを順番に制御しなくてもいい場合でも、まとめて実行してしまうと予想外のガバナ制限エラーが発生してしまうので注意です。