Async/await con modelli e store

Nell'articolo precedente abbiamo parlato in generale dell'uso delle Promise e della coppia async/await. Purtroppo ExtJS il supporto è limitato alle chiamate AJAX. Sarebbe molto interessante poter utilizzare questo tipo di sintassi anche per le letture/scritture sugli store ma anche sui modelli o per ottenere il risultato da una dialogbox.

Store

Gli store sono una delle classi più usate in ExtJS. Se l'unico scopo è visualizzare i dati da su una lista o una griglia non ci sono problemi, ma nel caso in cui si vogliano elaborare i dati una volta letti è necessarrio utlizzare una callback in questo modo:

store.load({
    scope: this,
    callback: function(records, operation, success) {
        // the operation object
        // contains all of the details of the load operation
        console.log(records);
    }
});

ExtJS fortunatamente ci dà la possibilità di modificare a piacimento anche classi esistenti tramite l'utilizzo di override. Possiamo quindi modificare il metodo load aggiungendo una configurazione, per esempio usePromise, che trasformi il valore di ritorno di store.load in una promise, in modo da poterla chiamare con await.

Ext.define('Fiddle.overrides.Store', {
   
    override: 'Ext.data.ProxyStore',

    load(options) {
        let me = this;
        if (options && options.usePromise) {
            delete options.usePromise;
            return new Ext.Promise(function (resolve, reject) {
                me.load(Ext.apply(options || {}, {
                    callback(records, operation, success) {
                        if (success) {
                            resolve(records);
                        } else {
                            reject(operation);
                        }
                    }
                }));
            });
        } else {
            return this.callParent(arguments);
        }
    }


});

In questo modo possiamo trasformare il codice precedente così:

const records = await store.load({ usePromise: true });
console.log(records);

L'unica accortezza è che la funzione dove sarà inserito quel codice dovrà essere dichiarata come async.

Dialog box

Con ExtJS anche il risultato di una semplice dialog box richiede una callback:

Ext.Msg.confirm('Info', 'Do you want to save changes?', function (buttonId) {
    if (buttonId === 'yes') {
        //...
    }
});

In questo caso probabilmente invece che usare un override ha più senso creare una nuova classe singleton:

Ext.define('Fiddle.Message', {
    singleton: true,
    confirm(message) {
        var me = this;
        return new Ext.Promise(function (resolve, reject) {
             Ext.Msg.confirm(document.title, message, function(buttonId) {
                resolve(buttonId);
            });
        });
    }
});

Notate che non è stato necessario usare il metodo reject della promise in quanto la dialog box non ha una condizione di errore.

In questo modo il codice precedente diventa questo:

const buttonId = await Fiddle.Message.confirm('Do you want to save changes?');
if (buttonId === 'yes') {
    //...
}

Conclusione

Usando una struttura simile si può aggiungere il supporto alle promise e async/await dovunque sia necessario rendendo il codice sicuramente più leggibile.

Potete provare gli esempi dell'articolo su questo fiddle.