5
votes

I'm developing an App Maker app and from a client-side script, I have to make calls to server side functions - suppose function a and function b - that are dependent on each other.

The first (native) solution in client-side script was the following, which uses callbacks:

function doB(s) {
    google.script.run.withSuccessHandler(function(result) { // async workload
        console.log(result);
    }).withFailureHandler(function(error) {
        console.log(error);
    }).b(s);
}

function aThenB(s) {
    google.script.run.withSuccessHandler(function(result) { // async workload
        console.log(result);
        doB(result); // call b
    }).withFailureHandler(function(error) {
        console.log(error);
    }).a(s);
}

But if you have to then call function c, and then d, etc, with the results of these related function executions, this becomes very unwieldy. I read this article, which describes converting from callback to Promise, e.g.:

function runnerToPromise(f) {
    var runArgs = Array.prototype.slice.call(arguments).slice(1);
    return new Promise(function(resolve, reject) { // executor
        google.script.run.withSuccessHandler(function(result) {
            resolve(result); // resolve promise
        }).withFailureHandler(function(error) {
            reject(error); // reject promise
        })[f].apply(this, runArgs); // async
    });
}

Which would make the above code look like:

function aThenB(s) {
    return runnerToPromise('a', s).then(
        function(result) {
            return runnerToPromise('b', result);
        }
    );
}

function runAThenB(s) {
    aThenB(s).then(function(result) {
        console.log('SUCCESS: ' + result);
    }).catch(function(error) {
        console.log(error);
    });
}

This solution works properly if you use Apps Script and HtmlService, and deploy the script as "Web App."

However, in App Maker, it throws an error that I cannot understand:

TypeError: Cannot read property 'withLogger' of undefined.

How can I resolve this App Maker-only error?


UPDATE

I made a new test app with App Maker to reproduce the problem.

The app just does that: it copies, in two steps, the data from a spreadsheet to another spreadsheet.

I ran the code in App Maker and I also deployed the code as a Web Application made with Apps Script.

No problem with the Web Application made with Apps Script; in App Maker only the solution that uses callbacks works properly.

Server side script:

function serverStepOne() {

    console.log('serverStepOne fn called!');

    return SpreadsheetApp

        .openById('XXX') // Spreadsheet A

        .getSheetByName('A')

        .getDataRange()

        .getValues();
}

function serverStepTwo(grid) {

    console.log('serverStepTwo fn called!');

    SpreadsheetApp

        .openById('YYY') // Spreadsheet B

        .getSheetByName('A').getRange(1, 1, grid.length, grid[0].length).setValues(grid); // all rows have the same number of cells

        return 'copy done!';
}

Client side script:

function copyData() { // messy one... works like a charm in App Maker

    google.script.run.withSuccessHandler(function(result) {

        google.script.run.withSuccessHandler(function(result) {console.log(result);}).withFailureHandler(function(error) {console.log(error);}).serverStepTwo(result);

    }).withFailureHandler(function(error) {console.log(error);}).serverStepOne();
}

function runnerToPromise(f) {

    var runArgs = Array.prototype.slice.call(arguments).slice(1);

    return new Promise(function(resolve, reject) {

        google.script.run.withSuccessHandler(function(result) {

            resolve(result);

        }).withFailureHandler(function(error) {

            reject(error);

        })[f].apply(this, runArgs);
    });
}

function copyDataPromise() { // tidy one... doesn't work in App Maker; works in Apps Script Web App

    return runnerToPromise('serverStepOne').then(

    function(result) {

        return runnerToPromise('serverStepTwo', result);
    });
}

function runCopyDataPromise() {

    copyDataPromise().then(function(result) {console.log(result);}).catch(function(error) {console.log(error);});
}

App Maker, when the copyDataPromise fn is called, produces the following log in the browser's console:

com.google.apps.appmaker.AppMakerGwt-0.js:7706 Uncaught (in promise) TypeError: Cannot read property 'withLogger' of undefined
at n.(n-omfqufplv3inu3qxlfsar7h6wn2rogznri3mjoq-0lu-script.googleusercontent.com/anonymous function) (com.google.apps.appmaker.AppMakerGwt-0.js:7706:182)
at clientScriptsContext.js:14
at <anonymous>:26:11
at new Promise (<anonymous>)
at runnerToPromise (<anonymous>:16:10)
at copyDataPromise (<anonymous>:32:10)
at COMPONENT_EVENT_1sVF9N7aiAUX2sXc4qZ90Ben2JowEfeez_0_1142335744_onClick (<anonymous>:48:1)
at window.am_exit (clientScriptsContext.js:22)
at UZ (com.google.apps.appmaker.AppMakerGwt-0.js:6651)
at LZ (com.google.apps.appmaker.AppMakerGwt-0.js:4950)
at NZ (com.google.apps.appmaker.AppMakerGwt-0.js:6043)
at Qgb (com.google.apps.appmaker.AppMakerGwt-0.js:6803)
at QTb.RTb [as sc] (com.google.apps.appmaker.AppMakerGwt-0.js:7738)
at ozd.pzd [as xd] (com.google.apps.appmaker.AppMakerGwt-0.js:7746)
at wCd (com.google.apps.appmaker.AppMakerGwt-0.js:7328)
at oCd (com.google.apps.appmaker.AppMakerGwt-0.js:6718)
at Id (com.google.apps.appmaker.AppMakerGwt-0.js:1394)
at Wyd (com.google.apps.appmaker.AppMakerGwt-0.js:6732)
at Kd (com.google.apps.appmaker.AppMakerGwt-0.js:6025)
at PHc.Vd [as dc] (com.google.apps.appmaker.AppMakerGwt-0.js:7728)
at pue (com.google.apps.appmaker.AppMakerGwt-0.js:5477)
at HTMLButtonElement.Vve (com.google.apps.appmaker.AppMakerGwt-0.js:4828)
at Drd (com.google.apps.appmaker.AppMakerGwt-0.js:2887)
at Grd (com.google.apps.appmaker.AppMakerGwt-0.js:6717)
at HTMLButtonElement.eval (com.google.apps.appmaker.AppMakerGwt-0.js:4677)
n.(anonymous function) @ com.google.apps.appmaker.AppMakerGwt-0.js:7706
(anonymous) @ clientScriptsContext.js:14
(anonymous) @ VM28:26
runnerToPromise @ VM28:16
copyDataPromise @ VM28:32
COMPONENT_EVENT_1sVF9N7aiAUX2sXc4qZ90Ben2JowEfeez_0_1142335744_onClick @ VM38:48
window.am_exit @ clientScriptsContext.js:22
UZ @ com.google.apps.appmaker.AppMakerGwt-0.js:6651
LZ @ com.google.apps.appmaker.AppMakerGwt-0.js:4950
NZ @ com.google.apps.appmaker.AppMakerGwt-0.js:6043
Qgb @ com.google.apps.appmaker.AppMakerGwt-0.js:6803
RTb @ com.google.apps.appmaker.AppMakerGwt-0.js:7738
pzd @ com.google.apps.appmaker.AppMakerGwt-0.js:7746
wCd @ com.google.apps.appmaker.AppMakerGwt-0.js:7328
oCd @ com.google.apps.appmaker.AppMakerGwt-0.js:6718
Id @ com.google.apps.appmaker.AppMakerGwt-0.js:1394
Wyd @ com.google.apps.appmaker.AppMakerGwt-0.js:6732
Kd @ com.google.apps.appmaker.AppMakerGwt-0.js:6025
Vd @ com.google.apps.appmaker.AppMakerGwt-0.js:7728
pue @ com.google.apps.appmaker.AppMakerGwt-0.js:5477
Vve @ com.google.apps.appmaker.AppMakerGwt-0.js:4828
Drd @ com.google.apps.appmaker.AppMakerGwt-0.js:2887
Grd @ com.google.apps.appmaker.AppMakerGwt-0.js:6717
(anonymous) @ com.google.apps.appmaker.AppMakerGwt-0.js:4677
Promise.then (async)
copyDataPromise @ VM28:32
COMPONENT_EVENT_1sVF9N7aiAUX2sXc4qZ90Ben2JowEfeez_0_1142335744_onClick @ VM38:48
window.am_exit @ clientScriptsContext.js:22
UZ @ com.google.apps.appmaker.AppMakerGwt-0.js:6651
LZ @ com.google.apps.appmaker.AppMakerGwt-0.js:4950
NZ @ com.google.apps.appmaker.AppMakerGwt-0.js:6043
Qgb @ com.google.apps.appmaker.AppMakerGwt-0.js:6803
RTb @ com.google.apps.appmaker.AppMakerGwt-0.js:7738
pzd @ com.google.apps.appmaker.AppMakerGwt-0.js:7746
wCd @ com.google.apps.appmaker.AppMakerGwt-0.js:7328
oCd @ com.google.apps.appmaker.AppMakerGwt-0.js:6718
Id @ com.google.apps.appmaker.AppMakerGwt-0.js:1394
Wyd @ com.google.apps.appmaker.AppMakerGwt-0.js:6732
Kd @ com.google.apps.appmaker.AppMakerGwt-0.js:6025
Vd @ com.google.apps.appmaker.AppMakerGwt-0.js:7728
pue @ com.google.apps.appmaker.AppMakerGwt-0.js:5477
Vve @ com.google.apps.appmaker.AppMakerGwt-0.js:4828
Drd @ com.google.apps.appmaker.AppMakerGwt-0.js:2887
Grd @ com.google.apps.appmaker.AppMakerGwt-0.js:6717
(anonymous) @ com.google.apps.appmaker.AppMakerGwt-0.js:4677

There are no errors at all in Stackdriver.


UPDATE 2

I've modified the code a little, adding named function expressions.

function runnerToPromise(f) {

    var runArgs = Array.prototype.slice.call(arguments).slice(1);

    return new Promise(function myExecutor(resolve, reject) {

        google.script.run.withSuccessHandler(function myWithSuccessHandler(result) {

            resolve(result);

        }).withFailureHandler(function myWithFailureHandler( error) {

            reject(error);

        })[f].apply(null, runArgs);
    });
}

function copyDataPromise() { // tidy one... doesn't work at all in App Maker; works with Apps Script (Web App)

    return runnerToPromise('serverStepOne').then(

        function myFirstPromiseThen(result) {

            return runnerToPromise('serverStepTwo', result);
        });
    }

    function runCopyDataPromise() {

        copyDataPromise().then(function myLastPromiseThen(result) {console.log(result);}).catch(function myFinalCatch(error) {console.log(error);});
}

Console log:

com.google.apps.appmaker.AppMakerGwt-0.js:formatted:57540 Uncaught (in promise) TypeError: Cannot read property 'withLogger' of undefined
    at n.(n-omfqufplv3inu3qxlfsar7h6wn2rogznri3mjoq-0lu-script.googleusercontent.com/anonymous function) (com.google.apps.appmaker.AppMakerGwt-0.js:7706:182)
    at clientScriptsContext.js:14
    at myExecutor (<anonymous>:26:11)
    at new Promise (<anonymous>)
    at runnerToPromise (<anonymous>:16:10)
    at copyDataPromise (<anonymous>:32:10)
    at COMPONENT_EVENT_1sVF9N7aiAUX2sXc4qZ90Ben2JowEfeez_0_1142335744_onClick (<anonymous>:48:1)
    at window.am_exit (clientScriptsContext.js:22)
    at UZ (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36031)
    at LZ (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:20165)
    at NZ (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:29107)
    at Qgb (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:38046)
    at QTb.RTb [as sc] (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:77339)
    at ozd.pzd [as xd] (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:93227)
    at wCd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:46855)
    at oCd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36933)
    at Id (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:3853)
    at Wyd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:37133)
    at Kd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:28919)
    at PHc.Vd [as dc] (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:61727)
    at pue (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:23899)
    at HTMLButtonElement.Vve (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:19342)
    at Drd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:9567)
    at Grd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36910)
    at HTMLButtonElement.eval (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:18425)
n.(anonymous function) @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:57540
(anonymous) @ clientScriptsContext.js:14
myExecutor @ VM28:26
runnerToPromise @ VM28:16
copyDataPromise @ VM28:32
COMPONENT_EVENT_1sVF9N7aiAUX2sXc4qZ90Ben2JowEfeez_0_1142335744_onClick @ VM72:48
window.am_exit @ clientScriptsContext.js:22
UZ @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36031
LZ @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:20165
NZ @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:29107
Qgb @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:38046
RTb @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:77339
pzd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:93227
wCd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:46855
oCd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36933
Id @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:3853
Wyd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:37133
Kd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:28919
Vd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:61727
pue @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:23899
Vve @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:19342
Drd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:9567
Grd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36910
(anonymous) @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:18425
Promise.then (async)
copyDataPromise @ VM28:32
COMPONENT_EVENT_1sVF9N7aiAUX2sXc4qZ90Ben2JowEfeez_0_1142335744_onClick @ VM72:48
window.am_exit @ clientScriptsContext.js:22
UZ @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36031
LZ @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:20165
NZ @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:29107
Qgb @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:38046
RTb @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:77339
pzd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:93227
wCd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:46855
oCd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36933
Id @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:3853
Wyd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:37133
Kd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:28919
Vd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:61727
pue @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:23899
Vve @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:19342
Drd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:9567
Grd @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36910
(anonymous) @ com.google.apps.appmaker.AppMakerGwt-0.js:formatted:18425

image 1


UPDATE 3

I've tried the code below. It works in an Apps Script Web App. It doesn't work in App Maker. In App Maker it throws the same error: Cannot read property 'withLogger' of undefined

/*jshint esnext:true*/

const runnerToPromiseSpread = (fn, ...args) => {

      return new Promise((resolve, reject) => {

        let task = google.script.run

          .withSuccessHandler(resolve)

          .withFailureHandler(reject);

        if (task[fn] === undefined) {

          reject("'" + fn + "' is not a global function in your Apps Script project");

        } else {

          task[fn].apply(null, args);
        }
      });
    };

    function copyDataPromiseSpread() {

      return runnerToPromiseSpread('serverStepOne')

        .then((result) => {

          return runnerToPromiseSpread('serverStepTwo', result);
        });
    }


    function runCopyDataPromiseSpread() {

      copyDataPromiseSpread().then((result) => {

        console.log(result);

      }).catch((error) => {

        console.log(error);
      });
    }

In the browser's console:

TypeError: Cannot read property 'withLogger' of undefined
at n.(n-omfqufplv3inu3qxlfsar7h6wn2rogznri3mjoq-0lu-script.googleusercontent.com/anonymous function) (com.google.apps.appmaker.AppMakerGwt-0.js:7706:182)
at clientScriptsContext.js:14
at <anonymous>:21:16
at new Promise (<anonymous>)
at runnerToPromiseSpread (<anonymous>:7:10)
at copyDataPromiseSpread (<anonymous>:28:10)
at runCopyDataPromiseSpread (<anonymous>:39:5)
at COMPONENT_EVENT_1sVF9N7aiAUX2sXc4qZ90Ben2JowEfeez_0_1142335744_onClick (<anonymous>:86:1)
at window.am_exit (clientScriptsContext.js:22)
at UZ (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36031)
at LZ (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:20165)
at NZ (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:29107)
at Qgb (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:38046)
at QTb.RTb [as sc] (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:77339)
at ozd.pzd [as xd] (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:93227)
at wCd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:46855)
at oCd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36933)
at Id (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:3853)
at Wyd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:37133)
at Kd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:28919)
at PHc.Vd [as dc] (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:61727)
at pue (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:23899)
at HTMLButtonElement.Vve (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:19342)
at Drd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:9567)
at Grd (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:36910)
at HTMLButtonElement.eval (com.google.apps.appmaker.AppMakerGwt-0.js:formatted:18425)
1
Can you provide more information about the error, such as the line number, associated call stack, console/stackdriver errors, and so on? - tehhowch
From here we see that this error means the object task runner (the google.script.run instance) has become undefined. So, you seemingly have some issue in associated code that you have not shared here. (Note: I have no access to AppMaker and cannot troubleshoot this further) - tehhowch
@tehhowch I've created a fake, simple app to reproduce the problem. I've updated the question. Thank you for editing and making the original question clearer! - sbibiz
Setting to null the thisArg for apply makes no difference. - sbibiz
Did you mean something like what I wrote in the third update? Thank you very much! - sbibiz

1 Answers

0
votes

The comment by @tehhowch was very helpful:

this error means the object task runner (the google.script.run instance) has become undefined.

Indeed, that is the problem inside appmaker. To fix this, I simply did this:

function runnerToPromise(f) {
  var runArgs = Array.prototype.slice.call(arguments).slice(1);
  return new Promise(function(resolve, reject) {
    var runner = google.script.run.withSuccessHandler(function(result) {
      resolve(result); 
    }).withFailureHandler(function(error) {
      reject(error); 
    });
    runner[f].apply(runner, runArgs); 
  });
}