Reputation: 7342
I have a client application that has javascript scripting support. The app is written in Typescript. While everything seems to work as expected, I have a problem when users try to use await in their scripts.
I rewrote the scenario in pure JS and done a fiddle of it. In this environment it works as expected, namely: Messageboxes come one after the other as the user presses the button.
This is the fiddle: http://jsfiddle.net/927mtdz8/42/
However, the Typescript code in the application when runs the script pops all Messageboxes at once seemingly like await is ignored.
I cannot figure out what is wrong. This is the stripped down TS code:
function makeScript(owner:string, text: string, argsSig: string) {
let _scriptFunc_: any;
let own = owner;
try {
const promiseCode = `let __res = null;let __rej = null;let __promise = new Promise((resolve, reject) => {__res = resolve;__rej = reject;});try {\n`;
const ending = `;\n;__res();} catch (err) { __rej(err);}; return __promise;`;
const scriptSource = "_scriptFunc_ = async function(" + argsSig + ") {\"use strict\";\n" + promiseCode + text + ending + "\n}";
eval(scriptSource);
}
catch (err) {
console.log(err);
return null;
}
const ret = _scriptFunc_.bind(this);
return ret;
}
async function messagebox(_text: string):Promise<string> {
let resolveFunc:Function = null;
let rejectFunc:Function = null;
let promise = new Promise<string>((resolve,reject) => {
resolveFunc = resolve;
rejectFunc = reject;
});
let win = document.createElement("div");
let $win = $(win);
document.body.appendChild(win);
win.innerHTML = `
<div id="messageboxtext"></div>
<button id="accept" style="width:200px;height:50px;">OK</button>
`;
const acceptButton = $("#accept", $win);
const $text = $("#messageboxtext", $win);
$text.text(_text);
$(acceptButton).click(() => {
resolveFunc("ok");
$win.remove();
});
$win.css({'position':'relative','width':'auto', 'height':'auto', 'background-color':'red'});
return promise
}
class Messagebox {
public static async ShowInput(_text: string): any {
return await messagebox(_text);
}
}
class ScriptBuilder {
public scriptThis:any;
public makeScript(owner:string, text: string, argsSig: string): any {
try {
const scr = makeScript.call(this.scriptThis, owner, text, argsSig);
return async (...args: any[]) => {
try {
let ret = await scr(...args);
return ret;
} catch (err) {
console.log(err);
}
};
} catch (err2) {
console.log(err2);
}
}
}
let scrBuilder = new ScriptBuilder();
scrBuilder.scriptThis = {Message:""};
(<any>window).Messagebox = Messagebox;
let _script = scrBuilder.makeScript("scriptName", `
this.Message += await Messagebox.ShowInput(question + ' 1 ');
console.log(this.Message);
this.Message += await Messagebox.ShowInput(question + ' 2 ');
console.log(this.Message);
this.Message += await Messagebox.ShowInput(question + ' 3 ');
console.log(this.Message);
`, "question");
_script("Please press the button");
Unfortunately, the TS code does not compile in JSFiddle, breaking the transpiled code, so I could not do a fiddle of it.
Basically it seems when the script function executes, the whole body of it runs at once like there were no awaits in it.
Does anybody know why the await seems to not be respected?
Typescript version 3.9.7 and this is the tsconfig:
{
"compilerOptions": {
"sourceMap": true,
"outDir": "./build",
"noImplicitAny": true,
"esModuleInterop": true,
"noImplicitReturns": true,
"target": "es5",
"downlevelIteration": true,
"lib": [
"DOM",
"ES6",
"DOM.Iterable",
"ScriptHost"
],
"types": [
"jquery"
]
},
"exclude": [
"node_modules"
],
"include": [
"./src/ts/*"
]
}
Edit: it seems it is something with my environment because it works in a StackBlitz app: https://stackblitz.com/edit/typescript-ldr5sm
Edit2: I tried to update the injected Promise code to make the script async, but even something like this does the same thing (does not respect the await):
async ƒ (match, line) {
let p = new Promise((resolve, reject) => {
(async () => {
try {
await Messagebox.ShowInput("...");
await Messagebox.ShowInput("...");
resolve('')
} catch (err) {
reject(err)
}
})()
});
return p;
}
Both messageboxes pop at the same time.
Upvotes: 2
Views: 496
Reputation: 7342
Sorry for the ghost hunting. The error was in the client usage.
The client did this:
this.something = await Messagebox.Show("...").toString();
Which is not awaiting of a function but of a toString(), which is synchronous. Weirdly javascript does not complain for this syntax, and I lost of lot of time to figure out because I did not have the exact code the user tried in the script.
Upvotes: 1