使用 Javascript async & await with google.script.url.getLocation
Using Javascript async & await with google.script.url.getLocation
我正在尝试重构 Google Apps Script 网络应用程序中的一些难看的代码,以便它使用 async
/ await
.
它使用 google.script.url.getLocation
客户端,提取 URL 参数,然后将它们发送给其他异步函数。
一定有办法优雅地做到这一点。
var doSomeAsyncShit =()=> {
google.script.url.getLocation(function (location) {
var rid = (location.parameter.rid) ? location.parameter.rid : defaultReportID;
var uid = (location.parameter.uid) ? location.parameter.uid : defaultUserID;
console.log (((location.parameter.rid) ? "Report #" : "Default Report ID #")+rid);
console.log (((location.parameter.uid) ? "User #" : "Default User ID #" )+uid);
google.script.run.withSuccessHandler(paintReport).returnJSON(rid);
google.script.run.withSuccessHandler(getMyReportsList).listMyReports(uid);
});
}
$(function () {
doSomeAsyncShit();
}
由于 Promise
可以用自定义执行函数构造,您可以将 google.script.url
包装到其中,并在需要时解析或拒绝。如果您随后将其设为效用函数,请使用 await
等待其解析。
下面是一个灵活的小实用程序,用于制作 google.script.url
async-friendly:
/**
* @typedef {{
* hash : string,
* parameter : Object.<string, string>,
* parameters : Object.<string, string[]>
* }} UrlLocationObject
*
* @typedef {{
* callback : function (UrlLocationObject, ...any) : any,
* params : any[]
* }} AsyncUrlOptions
*
* @summary Promise-friendly google.script.url
* @param {AsyncUrlOptions}
* @returns {Promise}
*/
const asyncLocation = ({
callback,
params = [],
}) => {
return new Promise((res, rej) => {
google.script.url.getLocation((loc) => {
try {
const result = callback(loc, ...params);
res(result);
}
catch(error) {
rej(error);
}
});
});
};
google.script.run
也是如此:
/**
* @typedef {{
* funcName : string,
* onFailure : function,
* onSuccess : function,
* params : array
* }} AsyncOptions
*
* @summary v2 of async-friendly google.script.run
* @param {AsyncOptions}
* @returns {Promise}
*/
const asyncGAPIv2 = ({
funcName,
onFailure = console.error,
onSuccess,
params = []
}) => {
return new Promise((res, rej) => {
google.script.run
.withSuccessHandler(data => {
typeof onSuccess === "function" && onSuccess(data);
res(data);
})
.withFailureHandler(error => {
typeof onFailure === "function" && onFailure(error);
rej(error);
})
[funcName].apply(null, params);
});
};
可以拦截对 google api 和直接 return Promise
using Proxy
的请求。
脚本:
/**
* Revives old client facing google api in apps script web applications
* Directly returns promises for `google.scipt.run` and `google.script.url.getLocation`
* @see
*/
(function projectAdrenaline_google() {
const lifeline = {
funcList: [],
excludeList: [
'withSuccessHandler',
'withFailureHandler',
'withUserObject',
'withLogger',
],
get: function(target, prop, rec) {
if (this.excludeList.includes(prop))
//return (...rest) => new Proxy(Reflect.apply(target[prop], target, rest), trap);
throw new TypeError(
`${prop}: This method is deprecated in this custom api`
);
if (this.funcList.includes(prop))
return (...rest) =>
new Promise((res, rej) =>
target
.withSuccessHandler(res)
.withFailureHandler(rej)
[prop](...rest)
);
switch (prop) {
case 'run':
this.funcList = Object.keys(target.run);
break;
case 'getLocation':
return () => new Promise(res => target[prop](res));
}
return new Proxy(Reflect.get(target, prop, rec), lifeline);
},
};
//const superGoogle = new Proxy(google, trap);
//OR overwrite currently loaded google object:
google = new Proxy(google, lifeline);
})();
示例:
const doSomeAsyncStuff = async () => {
const location = await google.script.url.getLocation();
const rid = location.parameter.rid ? location.parameter.rid : defaultReportID;
const uid = location.parameter.uid ? location.parameter.uid : defaultUserID;
//promise
google.script.run.returnJSON(rid).then(paintReport);
//async-await
const reportsList = await google.script.run.listMyReports(uid);
getMyReportsList(reportsList);
};
或者,可以将函数用作语法糖。但这需要学习新的语法定义:
/**
* Syntactic sugar around old callback api returning a promise
*
* @returns {promise} Promise of call from server
* @param {string[]|string} propertyAccesors Array of properties to access
* @param {object[][]} methodAccesors Array of [method_to_access,arguments[]]
* @param {number[]} resRejIdxs 2 Indexes of methodAccesors corresponding to resolve/success and rejection/failure. If omitted promise is resolved immediately.
*/
const GS = (propertyAccesors, methodAccesors, resRejIdxs) =>
new Promise((res, rej) => {
//Boilerplate for type correction
const nestArray = e => (Array.isArray(e) ? e : [e]);
propertyAccesors = nestArray(propertyAccesors);
methodAccesors = nestArray(methodAccesors);
methodAccesors[0] = nestArray(methodAccesors[0]);
if (typeof resRejIdxs !== 'undefined') {
resRejIdxs = Array.isArray(resRejIdxs) ? resRejIdxs : [resRejIdxs];
resRejIdxs[0] && (methodAccesors[resRejIdxs[0]][1] = res);
resRejIdxs[1] && (methodAccesors[resRejIdxs[1]][1] = rej);
} else {
res('Done');
}
//Access properties and call methods
methodAccesors.reduce(
(acc, [method, methodArg]) =>
Array.isArray(methodArg)
? acc[method](...methodArg)
: acc[method](methodArg),
propertyAccesors.reduce(
(acc, currentProp) => acc[currentProp],
google.script
)
);
});
//EXAMPLES:
GS(
'run',
[
['withSuccessHandler', null],
['callServer', [5, 4]], //call server function `callServer` with 2 arguments 5 and 4
['withFailureHandler', null],
],
[0, 2] //0 is withSuccessHandler and 2 is withFailureHandler
).then(alert);
GS('history', [['setChangeHandler', e => console.log(e.location.hash)]]);
GS('url', 'getLocation', 0).then(location => console.log(location.hash));
GS(['host', 'editor'], 'focus');
GS('host', ['setHeight', 50]);
这样的事情还不错:
var doSomeAsyncShit = async () => {
let location = await new promise(resolve => google.script.url.getLocation(resolve))
// do stuff with location
}
(async () => {
await doSomeAsyncShit();
// do something after
})()
即便如此,您还是增加了复杂性,降低了可读性,并且毫无理由恕我直言
我正在尝试重构 Google Apps Script 网络应用程序中的一些难看的代码,以便它使用 async
/ await
.
它使用 google.script.url.getLocation
客户端,提取 URL 参数,然后将它们发送给其他异步函数。
一定有办法优雅地做到这一点。
var doSomeAsyncShit =()=> {
google.script.url.getLocation(function (location) {
var rid = (location.parameter.rid) ? location.parameter.rid : defaultReportID;
var uid = (location.parameter.uid) ? location.parameter.uid : defaultUserID;
console.log (((location.parameter.rid) ? "Report #" : "Default Report ID #")+rid);
console.log (((location.parameter.uid) ? "User #" : "Default User ID #" )+uid);
google.script.run.withSuccessHandler(paintReport).returnJSON(rid);
google.script.run.withSuccessHandler(getMyReportsList).listMyReports(uid);
});
}
$(function () {
doSomeAsyncShit();
}
由于 Promise
可以用自定义执行函数构造,您可以将 google.script.url
包装到其中,并在需要时解析或拒绝。如果您随后将其设为效用函数,请使用 await
等待其解析。
下面是一个灵活的小实用程序,用于制作 google.script.url
async-friendly:
/**
* @typedef {{
* hash : string,
* parameter : Object.<string, string>,
* parameters : Object.<string, string[]>
* }} UrlLocationObject
*
* @typedef {{
* callback : function (UrlLocationObject, ...any) : any,
* params : any[]
* }} AsyncUrlOptions
*
* @summary Promise-friendly google.script.url
* @param {AsyncUrlOptions}
* @returns {Promise}
*/
const asyncLocation = ({
callback,
params = [],
}) => {
return new Promise((res, rej) => {
google.script.url.getLocation((loc) => {
try {
const result = callback(loc, ...params);
res(result);
}
catch(error) {
rej(error);
}
});
});
};
google.script.run
也是如此:
/**
* @typedef {{
* funcName : string,
* onFailure : function,
* onSuccess : function,
* params : array
* }} AsyncOptions
*
* @summary v2 of async-friendly google.script.run
* @param {AsyncOptions}
* @returns {Promise}
*/
const asyncGAPIv2 = ({
funcName,
onFailure = console.error,
onSuccess,
params = []
}) => {
return new Promise((res, rej) => {
google.script.run
.withSuccessHandler(data => {
typeof onSuccess === "function" && onSuccess(data);
res(data);
})
.withFailureHandler(error => {
typeof onFailure === "function" && onFailure(error);
rej(error);
})
[funcName].apply(null, params);
});
};
可以拦截对 google api 和直接 return Promise
using Proxy
的请求。
脚本:
/**
* Revives old client facing google api in apps script web applications
* Directly returns promises for `google.scipt.run` and `google.script.url.getLocation`
* @see
*/
(function projectAdrenaline_google() {
const lifeline = {
funcList: [],
excludeList: [
'withSuccessHandler',
'withFailureHandler',
'withUserObject',
'withLogger',
],
get: function(target, prop, rec) {
if (this.excludeList.includes(prop))
//return (...rest) => new Proxy(Reflect.apply(target[prop], target, rest), trap);
throw new TypeError(
`${prop}: This method is deprecated in this custom api`
);
if (this.funcList.includes(prop))
return (...rest) =>
new Promise((res, rej) =>
target
.withSuccessHandler(res)
.withFailureHandler(rej)
[prop](...rest)
);
switch (prop) {
case 'run':
this.funcList = Object.keys(target.run);
break;
case 'getLocation':
return () => new Promise(res => target[prop](res));
}
return new Proxy(Reflect.get(target, prop, rec), lifeline);
},
};
//const superGoogle = new Proxy(google, trap);
//OR overwrite currently loaded google object:
google = new Proxy(google, lifeline);
})();
示例:
const doSomeAsyncStuff = async () => {
const location = await google.script.url.getLocation();
const rid = location.parameter.rid ? location.parameter.rid : defaultReportID;
const uid = location.parameter.uid ? location.parameter.uid : defaultUserID;
//promise
google.script.run.returnJSON(rid).then(paintReport);
//async-await
const reportsList = await google.script.run.listMyReports(uid);
getMyReportsList(reportsList);
};
或者,可以将函数用作语法糖。但这需要学习新的语法定义:
/**
* Syntactic sugar around old callback api returning a promise
*
* @returns {promise} Promise of call from server
* @param {string[]|string} propertyAccesors Array of properties to access
* @param {object[][]} methodAccesors Array of [method_to_access,arguments[]]
* @param {number[]} resRejIdxs 2 Indexes of methodAccesors corresponding to resolve/success and rejection/failure. If omitted promise is resolved immediately.
*/
const GS = (propertyAccesors, methodAccesors, resRejIdxs) =>
new Promise((res, rej) => {
//Boilerplate for type correction
const nestArray = e => (Array.isArray(e) ? e : [e]);
propertyAccesors = nestArray(propertyAccesors);
methodAccesors = nestArray(methodAccesors);
methodAccesors[0] = nestArray(methodAccesors[0]);
if (typeof resRejIdxs !== 'undefined') {
resRejIdxs = Array.isArray(resRejIdxs) ? resRejIdxs : [resRejIdxs];
resRejIdxs[0] && (methodAccesors[resRejIdxs[0]][1] = res);
resRejIdxs[1] && (methodAccesors[resRejIdxs[1]][1] = rej);
} else {
res('Done');
}
//Access properties and call methods
methodAccesors.reduce(
(acc, [method, methodArg]) =>
Array.isArray(methodArg)
? acc[method](...methodArg)
: acc[method](methodArg),
propertyAccesors.reduce(
(acc, currentProp) => acc[currentProp],
google.script
)
);
});
//EXAMPLES:
GS(
'run',
[
['withSuccessHandler', null],
['callServer', [5, 4]], //call server function `callServer` with 2 arguments 5 and 4
['withFailureHandler', null],
],
[0, 2] //0 is withSuccessHandler and 2 is withFailureHandler
).then(alert);
GS('history', [['setChangeHandler', e => console.log(e.location.hash)]]);
GS('url', 'getLocation', 0).then(location => console.log(location.hash));
GS(['host', 'editor'], 'focus');
GS('host', ['setHeight', 50]);
这样的事情还不错:
var doSomeAsyncShit = async () => {
let location = await new promise(resolve => google.script.url.getLocation(resolve))
// do stuff with location
}
(async () => {
await doSomeAsyncShit();
// do something after
})()
即便如此,您还是增加了复杂性,降低了可读性,并且毫无理由恕我直言