Gestire in modo sincrono le funzioni asincrone
Utilizzare Promise per sincronizzare le funzioni in Google Apps Script
Scrivendo codice per la realizzazione delle nostre app ci imbattiamo spesso in situazioni in cui abbiamo bisogno di invocare diverse funzioni con l'esigenza che queste seguano un ordine specifico, es: f1 -> f2 -> f3 ... fn, ovvero che la successiva necessiti che la precedente le fornisca un risultato prima di poter essere eseguita.
Invocando le funzioni normalmente in modo distinto e ordinato, nel caso di chiamate asincrone, non sempre è garantito il successo dell'operazione. L'approccio più semplice da utilizzare in Javascript è quello delle callback, ovvero annidando la successiva funzione all'interno della callback della funzione precedente così da garantirsi che la stessa venga eseguita nel momento in cui la precedente ha completato le relative operazioni.
Un esempio di quanto appena esposto (al solo scopo illustrativo) è il seguente:
funzione1(function() {
console.log("fatto1");
funzione2(function() {
console.log("fatto2");
funzione3(function() {
console.log("fatto3");
funzioneTotale(function() {
console.log("Tutte le funzioni sono state eseguite secondo l'ordine richiesto!");
})
})
})
});
Quello che ne emerge è il cosidetto callback hell, ossia è un codice poco chiaro e di difficile manutenzione (soprattutto al crescere del numero delle funzioni annidate e delle relative operazioni), è preferibile pertanto trovare una strada alternativa, ed ecco che ci vengono incontro le promise.
Una promise è un'operazione che, come dice il nome stesso, 'promette' di restituire un valore in futuro.
Lato tecnico per creare una promise è necessario l'uso del comando new Promise che richiede una funzione da eseguire che accetti due parametri: resolve e reject. Il primo verrà utilizzato in caso di successo, il secondo in caso di errore.
L'esempio seguente mostra il funzionamento di una promise in Javascript:
let funzione1 = function(){
return new Promise(function(resolve,reject){
resolve(console.log("fatto1"));
});
}
let funzione2 = function(){
return new Promise(function(resolve,reject){
resolve(console.log("fatto2"));
});
}
let funzione3 = function(){
return new Promise(function(resolve,reject){
resolve(console.log("fatto3"));
});
}
let funzioneTotale = function(){
funzione1()
.then(funzione2)
.then(funzione3)
.then(function(){
console.log("Tutte le funzioni sono state eseguite secondo l'ordine richiesto!");
});
}
funzioneTotale();
Come evidenzia il codice di cui sopra, una promise prevede una funzione then() che sarà eseguita al completamento della promise (chiamata in precedenza) in modo da assicurare la sequenzialità delle operazioni.
In Google Apps Script le chiamate asincrone da un client HtmlService verso il server vengono effettuate usando google.script.run, l'esempio seguente mostra come utilizzare Promise per effettuare un'operazione asincrona (nel caso specifico recuperare il numero di mail nella propria casella di posta per poi mostrarne il valore all'interno di un div definito. L'esempio è banale ma l'obiettivo è mostrarne il concetto che ne sta alla base):
Codice .gs
function doGet() {
return HtmlService.createHtmlOutputFromFile('index');
}
function recuperaThreadsDaGmail() {
var threads = GmailApp.getInboxThreads();
return threads.length;
}
Codice .html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button id='button1'>Mostra il numero di Threads</button>
<div id='text1'></div>
</body>
<script>
function getThreads() {
var p=new Promise(function(resolve, reject) {
google.script.run.withSuccessHandler(
function(res) {
resolve(res);
}).recuperaThreadsDaGmail();
});
return p;
}
button1.addEventListener('click',
function(event) {
getThreads().then(function(res) {
text1.innerHTML=res.toString();
});
});
</script>
</html>
Dalle righe di codice di cui sopra è possibile notare che al click sul bottone viene eseguita la funzione 'getThreads().then(...', dove in getThreads() viene creata una promise, che effettua una chiamata lato server con google.script.run, al fine di attenderne il risultato per poi, then, mostrarlo a video.
E' proprio il caso di dirlo... promessa mantenuta!
Per gli amanti di jQuery ho creato un apposito tutorial che, basandosi sullo stesso esempio appena visto, mostra come utilizzare le promise avvalendosi della famosa libreria Javascript: Utilizzare jQuery e Promise in Google Apps Script.
Michele sei un grande! La rendi molto piu semplice (e chiara). Grazie per quello che fai.
Grazie a te per il feedback Greg :)
Buongiorno Michele, innanzitutto complimenti. È possibile con questo sistema gestire la sincronizzazione AppSheet / Foglio Google una sola volta ogni ora dato che ogni ora sul foglio si attivano diversi trigger. Ti ringrazio in anticipo.
Ciao Marcello, grazie del feedback :) non ho capito se hai provato ad applicare la funzione e se hai ricevuto un errore. Data la specificità del caso che hai descritto direi che l'unico modo per capirlo è fare un test, se ricevi un errore particolare fammi sapere che lo analizziamo :)
Scusami Michele, chiedevo se era possibile farlo, io non ho la minima idea ... Ho messo insieme un progetto con gli strumenti di Google (form, foglio, alcuni script, una estensione ed infine AppSheet) che lavorano insieme in maniera “artigianale”, non so come “ottimizzarlo”; avevo pensato che se AppSheet sincronizzasse i dati su foglio con uno stop/Start di 10 minuti quando lavorano gli altri potrebbe semplificarsi tutto (trigger e filter)
Ciao Marcello,
la questione in questo caso è che AppSheet non è una piattaforma connessa in modo continuo, per questo motivo non c'è modo di richiedere una sincronizzazione se non tramite l'interazione dell'utente o aspettando la sincronizzazione automatica dopo 30 minuti. Se l'utente apporta modifiche ai dati e il dispositivo è online (e l'opzione degli aggiornamenti automatici è abilitata), i dati dell'app verranno sincronizzati automaticamente in pochi istanti.