2

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.

6
  • 1
    Have you checked the emitted JavaScript? What version of JS are you --target-ing? BTW if you're trying to get a runnable TS thing to share you might want to try StackBlitz or something like it instead of JSFiddle Commented Oct 4, 2020 at 13:39
  • Typescript 3.9.7, i+ll update the question with more info. Commented Oct 4, 2020 at 13:42
  • That promiseCode and ending in the compiled scriptSource don't make any sense (and break await in the text). Commented Oct 4, 2020 at 13:45
  • Does this work for you? If so, then it's not the code but your environment that's the problem. Ideally you would provide a minimal reproducible example that others can just look at to see the issue. Otherwise it's going to be a lot of guessing and sight-unseen remote debugging. Commented Oct 4, 2020 at 13:48
  • @Bergi no it is not breaking anything: I added an edit with a working example in StackBliitz. Seems something with my enviroment is causing it. Commented Oct 4, 2020 at 13:56

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.