Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ES2022: Top-level await proposal #1365

Closed
azu opened this issue Dec 24, 2021 · 9 comments · Fixed by #1374
Closed

ES2022: Top-level await proposal #1365

azu opened this issue Dec 24, 2021 · 9 comments · Fixed by #1374
Labels
Lang: ES2022 ECMAScript 2022

Comments

@azu
Copy link
Collaborator

azu commented Dec 24, 2021

Top-level await proposal

対応がいる。

あんまりユースケースでは使ってないので、詳しくは触れないかも。
CDNとかDynamic Importと合わせた場合 と コンソールが一番のユースケースな気はする。

Originally posted by @azu in #1337 (comment)

@azu azu added the Lang: ES2022 ECMAScript 2022 label Dec 24, 2021
@azu azu mentioned this issue Dec 24, 2021
17 tasks
@azu
Copy link
Collaborator Author

azu commented Jan 2, 2022

主なユースケース

tc39/proposal-top-level-await: top-level await proposal for ECMAScript (stage 4)

  • mainの即時実行関数のasync functionを消せる
    • エントリポイントーとかでたまに使う
(async function main(){

})();
  • promise自体をexportするケースがなくせる (dynamic importした結果をexport)
// awaiting.mjs
import { process } from "./some-module.mjs";
let output;
export default (async () => {
  const dynamic = await import(computedModuleSpecifier);
  const data = await fetch(url);
  output = process(dynamic.default, data);
})();
export { output };

後者のユースケースは、普通に関数をexportした方が圧倒的に良いので、扱いたくない。
単純にわかりにくいし、正当な感じはしない。

// awaiting.mjs
import { process } from "./some-module.mjs";
let output;
export const fetch = async () => {
  const dynamic = await import(computedModuleSpecifier);
  const data = await fetch(url);
  return process(dynamic.default, data);
}

あとは、コンソールでもDynamic ImportをTop-Levelにかけるのは、これのおかげなはず。
デバッグとか開発向け。

>  await import(`/i18n/${navigator.language}`);

mainをなくせるのはいいけど、正直なくてもそこまで困らない。
テスト的にあえて関数にした方がわかりやすいとか、スタックトレース的に別にTop-Level awaitのメリットがあるかどうか。
https://jsprimer.net/use-case/ajaxapp/promise/

@azu
Copy link
Collaborator Author

azu commented Jan 2, 2022

@azu
Copy link
Collaborator Author

azu commented Jan 2, 2022

Dynamic ImportのユースケースがないとTop-Level awaitって機能しにくいな。
CDNからimportするケースをどこかにたすとか結構現実的な気はする

@azu
Copy link
Collaborator Author

azu commented Jan 2, 2022

import confetti from 'https://cdn.skypack.dev/canvas-confetti';
confetti();

うーん、でもブラウザは元々URLをimportできるから、別にDynamic Import自体は関係ないか。

const confetti = await import('https://cdn.skypack.dev/canvas-confetti');
confetti.default();

こう書けても何も嬉しくない…

const version = "1.0.0"
const confetti = await import(`https://cdn.skypack.dev/canvas-confetti@${version}`);
confetti.default();

みたいなケースぐらいか。

@azu azu assigned azu and unassigned azu Jan 2, 2022
@azu
Copy link
Collaborator Author

azu commented Jan 2, 2022

https://jsprimer.net/use-case/ajaxapp/promise/#rewrite-to-async-function でasyncで囲んでるところをTop-Levelにする?

こちらは 結局 main() を呼び出してるのでやる必要性はなさそう。

@azu
Copy link
Collaborator Author

azu commented Jan 2, 2022

Top-Level await

仕様的には

  • Top-Levelに await 式が含まれる なら
  • そのModuleの [[Async]] 内部プロパティを trueにする

という仕組みなっている。
そのため、厳密にはawaitがあるならTop-LevelがAsync Functionになるという意味ではない という理解。

https://tc39.es/proposal-top-level-await/#sec-parsemodule

  • [[Async]] が true oe falseで実行方法が分岐する

https://tc39.es/proposal-top-level-await/#sec-source-text-module-record-execute-module

  • Top-Level awaitとは関係なく Moduleの Evaluate() はPromiseを返すようになった

https://tc39.es/proposal-top-level-await/#_ref_103

  • await 式は次の条件のTop-Levelにおける。それ以外はパースエラー
    • In an AsyncFunctionBody.
    • In the FormalParameters of an AsyncFunctionDeclaration, AsyncFunctionExpression, AsyncGeneratorDeclaration, or AsyncGeneratorExpression. AwaitExpression in this position is a Syntax error via static semantics.
    • When Module is the syntactic goal symbol.
  • 最後のModuleが今回追加されたTop-Level await で利用できるようになった await

https://tc39.es/proposal-top-level-await/#sec-async-function-definitions

@azu
Copy link
Collaborator Author

azu commented Jan 2, 2022

仕様的に見ると

await式がおけるのはAsyncFunctionとModuleのTop-Levelにだけおける

という表現になりそう。(FormalParametersとかは細かいことなので特に明記はしないけど)

@azu
Copy link
Collaborator Author

azu commented Jan 2, 2022

問題はModuleという表現は https://jsprimer.net/basic/module/#module なので、非同期より後にあるんだよなー
https://jsprimer.net/basic/introduction/#script-module で若干紹介してるので、ここにModuleのみでしか使えない機能があるよって感じの少したすぐらいかな

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>top-level-await-dynamic-import in script</title>
</head>
<body>
<script>
    console.log("script context");
    const confetti = await import('https://cdn.skypack.dev/canvas-confetti');
    confetti.default();
</script>
</body>
</html>

はエラーで動かない
https://admiring-kepler-7d35e9.netlify.app/script.html

  • コード実行の仕組みもModule対応が必要

@azu
Copy link
Collaborator Author

azu commented Jan 2, 2022

Top-Level awaitはScriptじゃなくてModuleじゃないと実行できない。
evalするときにModuleとして実行する方法。

evalだとModuleじゃないのでTop-Level awaitが利用できない
https://admiring-kepler-7d35e9.netlify.app/eval-script.html

await import(data:text/javascript;charset=utf-8, ${encodeURIComponent(src)})
これならModuleとしてevalでできるっぽい。
https://admiring-kepler-7d35e9.netlify.app/eval-module-in-script.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Lang: ES2022 ECMAScript 2022
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant