Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

186
Views
Why does await eval('`${await foo("xxx")}`') throw an error

Before I get flamed for using eval()... YES, I know eval === evil and from the devil and should never be used. That is NOT the question here. Believe it or not, I actually have a valid, real life case where it makes perfect sense to use eval, as a matter of fact, there is no other way to achieve the result I require.

Below is a code snippet with 4 cases. The first three works exactly as expected. The first two calls an async function in a template literal. The 3rd and 4th stores the template literal as a string and then eval the string. In the case (no 3) where await is not used, as expected the promise is returned. The problem is however case 4. It throws an error and for the life of me, I can't figure out why, or how to get around it.

I'm hoping someone smarter than me can either come up with a solution so that case 4 can still be evaluated without throwing an error (first prize) or explain why it doesn't work/can't be done.

async function foo(str) {
    return str.toUpperCase();
}

async function bar() {
    const str1 = `${foo("xxx")}`;

    console.log("STR1:", str1);
    // Output: STR1: [object Promise]

    const str2 = `${await foo("xxx")}`;

    console.log("STR2:", str2);
    // Output: STR2: XXX

    const str3 = '`${foo("xxx")}`';

    console.log("STR3:", await eval(str3));
    //Output STR3: [object Promise]

    const str4 = '`${await foo("xxx")}`';

    console.log("STR4:", await eval(str4));
    // undefined:1
    // `${await foo("xxx")}`
    //    ^^^^^^
    // Output: SyntaxError: Missing } in template expression
}

bar();

about 4 years ago · Juan Pablo Isaza
3 answers
Answer question

0

Another way of making it work, basically by separating the awaiting of the promise from the construction of the string (kind of), using tagged templates is described below:

async function foo(str) {
    return str.toUpperCase();
}

async function asyncFooTmpl(strings, asyncExpr) {
  return `${strings[0]}${await asyncExpr}${strings[1]}`;
}

async function bar() {
    const str1 = `${foo("xxx")}`;

    console.log("STR1:", str1);
    // Output: STR1: [object Promise]

    const str2 = `${await foo("xxx")}`;

    console.log("STR2:", str2);
    // Output: STR2: XXX

    const str3 = '`${foo("xxx")}`';

    console.log("STR3:", await eval(str3));
    //Output STR3: [object Promise]

    const str4 = 'asyncFooTmpl`${foo("xxx")}`';

    console.log("STR4:", await eval(str4));
    //Output STR4: XXX
}

bar();

This transfers the ownership of awaiting the promise from bar to asyncFooTmpl, allowing the string to be produced.

That said, it would require you to write functions for every template you have, or create a function that makes dealing with tagged templates more manageable.

Since you accepted this, and wrote your own answer combining my comment, I created a fiddle of a somewhat more improved example if you're curious.

about 4 years ago · Juan Pablo Isaza Report

0

I've tried many approaches and while I often came close to a solution, it never quite worked. The solution proposed by Heretic Monkey worked like a charm. I literally copied his code for "genericizing" the template function and it worked. Did not need to change a thing.

In case someone else is looking for a solution such as this, I'm posting the full example I put together to prove to myself that it works. The example is a bit long but I wanted to show a practical, real world use of the solution to make it easier of others to understand.

async function foo(str) {
    await sleep(2000);
    console.log(`foo caled with ${str}`);
    return str.toUpperCase();
}

function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

async function resolveTemplate(strings, ...maybePromises) {
    const final = [];
    for (let i = 0; i < strings.length; i++) {
        final.push(strings[i]);
        final.push(Object.prototype.toString.call(maybePromises[i]).includes("Promise") ? await maybePromises[i] : maybePromises[i]);
    }
    return final.join("");
}

const person = { firstName: "Joe", lastName: "Blogs", Age: 23 };

async function bar() {
    let str = 'resolveTemplate`{ "name": "${foo(person.firstName.concat(" ",person.lastName))}", "age": ${person.Age}, "sex": "${foo("male")}" }`';

    console.log("STR4:", await eval(str));
    //Output STR4: { "name": "JOE BLOGS", "age": 23, "sex": "MALE" }
}

bar();

console.log("Done");

about 4 years ago · Juan Pablo Isaza Report

0

It's not related to eval directly. You just can't use await outside of async function.

This will result in the same error:

const x = `${await Promise.resolve()}`

Wrapping the call in async IIFE solves the issue:

async function foo(str) {
  return str.toUpperCase();
}

async function bar() {
  const x = '`${(async () => await foo("xxx"))()}`'
  eval(x)
}

bar();

Proof that await can't be used in eval in async function, because eval isn't that simple

async function foo(str) {
  return str.toUpperCase();
}

async function bar() {
  try {
    const x = ' await foo("xxx")`'
    eval(x)
  } catch (e) {
    console.error(e)
  }
}

bar();

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!