初めに
前回の続きです。
Memo
Actions/States of Promise
:
(Unsettled)Pending
⇒neither onFulfilled or onRejected
(return resolve()⇒Settled)Fulfilled
⇒onFulfilled(.then(onFulfilled))
(return reject()/unexpected⇒Settled)Rejected
⇒onRejected(.then(..., onRejected)/.catch())
async function
やっとですね。async
とawait
のおかげで画期的な展開が迎えるようになりました。
Thunk
やco
の理解は一苦労してたけれど、co
の使い方では幅広いデータタイプでもとても快く対応してくれています。しかし依然として改善される余地があれば向上が止まらないのです。
co
はyield
にThunk
かPromise
しか受け入れられないことに対し、
async
はawait
に解決されたPromise
か拒否されたPromise
、あるいは Promise
ではない値なら暗黙にPromise
にしてから別のタスクキューに移させることによって非同期処理を実現します。
下は参考文章から取ったデモコードです。(一部練習のため変更あり)
// example1
async function getStockPriceByName(name) {
// function getStockName(str) {...}
// function getStockPrice(stockName) {...}
const stock = await getStockName(name);
const stockPrice = await getStockPrice(stock);
return stockPrice;
}
getStockPriceByName('goog').then((result) => {
console.log(result);
});
// example2
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function waitToPrint(value, ms) {
await timeout(ms);
console.log(value);
}
waitToPrint('hello world', 1000);
// or
async function timeout(ms) {
await new Promise((resolve) => {
setTimeout(resolve, ms); // after "ms" seconds, execute resolve()
});
}
async function waitToPrint(value, ms) {
await timeout(ms);
console.log(value);
}
// example3 - async function always returns promise
async function foo() {
return 'abc'
}
foo().then((result) => {
console.log(result);
});
// abc
// example4 - Error handling
async function foo() {
throw new TypeError('Typo');
}
foo().then(
result => console.log('resolve', result),
error => { if (error instanceof TypeError) console.log('reject', error) }
)
下は書き方のデモコードです。
// usage
// function declaration
async function foo() { }
// function expression
const foo = async function () { };
// arrow function
const foo = async () => { };
// object method
let obj = {
async foo() {
// something
}
};
obj.foo().then();
// class method
class Storage {
constructor() {
// CacheStorage.open()
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jude').then();
async function with then()
async
関数では内部コードすべての実行が完了し、返り値が解決されたPromise
か拒否されたPromise
だと約束されている。
// demo
async function getTitle(url) {
// get the response
let response = await fetch(url);
// wait until text is ready
let html = await response.text();
// return html title
// return html.match(/<title>([\s\S]+)<\/title>/i)[1]; // the result is unexpected
return html.match(/<title>([^<]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(
result => console.log(result),
err => console.log(err)
);
// ECMAScript® 2023 Language Specification
// debugger
// this is the result when i use ".match(/<title>([\s\S]+)<\/title>/i)[1]"
// let str = `<title>ECMAScript® 2023 Language Specification</title><script src="ecmarkup.js?cac
// he=YeiTF_tp" defer=""></script></head><body><div id="shortcuts-help">
// <ul>
// <li><span>Toggle shortcuts help</span><code>?</code></li>
// <li><span>Toggle "can call user code" annotations</span><code>u</code></li>
// <li><span>Navigate to/from multipage</span><code>m</code></li>
// <li><span>Jump to search box</span><code>/</code></li>
// </ul></div><div id="menu-toggle"><svg xmlns="http://www.w3.org/2000/svg" style="
// width:100%; height:100%; stroke:currentColor" viewBox="0 0 120 120">
// <title>Menu`
// console.log(str.match(/<title>([\s\S]+)<\/title>/i)[1]);
// ECMAScript® 2023 Language Specification
// note: in nodejs, it does work very well, maybe the problem occurred in fetch?
await
普通の関数でもPromise
を返してからthen()
でコールバックを呼び出すことができますがawait
はasync
関数以外は作用しない。普通の関数ではawait
を通してresolve
されたPromise
を待ったり、あるいはreject
されたPromise
のエラー処理したりするのができません。
一つ誤解されやすいところだと思いますが。
The expression is resolved in the same way as Promise.resolve(), which means thenable objects are supported, and if expression is not a promise, it's implicitly wrapped in a Promise and then resolved.
それはawait
を使えばPromise
でない値をPromise
オブジェクトに変換してくれるところです。
確かにthenable
オブジェクトではカスタムのthen((resolve, reject))
メソッドを利用すれば解決されたPromise
として返し、それからも.then()
や.catch()
でPromise
チェイン続くことができる。
しかしawait
本当の作用は、Promise
でない値をPromise
オブジェクトに包んでMicrotask
へ移行して非同期処理を行わせ、返り値を待つことです。詳しくは thenable vs. Promise (Microtask) のコードや解釈を参考にしていただければ幸いです。
// is Promise
async function isPromise1() {
let data = new Promise((resolve, reject) => {
resolve('abc');
});
console.log(data instanceof Promise);
data.then((result) => console.log(result));
}
// is Promise
async function isPromise2() {
let data = Promise.resolve('abc');
console.log(data instanceof Promise);
data.then((result) => console.log(result));
}
// not Promise
async function notPromise1() {
let data = await new Promise((resolve, reject) => {
resolve('abc');
});
console.log(data instanceof Promise);
// data.then((result) => console.log(result));
// TypeError: data.then is not a function
}
// not Promise
async function notPromise2() {
let data = await Promise.resolve('abc');
console.log(data instanceof Promise);
// data.then((result) => console.log(result));
// TypeError: data.then is not a function
}
isPromise1(); // true
isPromise2(); // true
notPromise1(); // false
notPromise2(); // false
await
から値がアサインされた変数はMicrotask Queue
から戻った値であり、Promise
インスタンスではないためthen()
メソッドもとうぜん使えなくなる。
変数ではなく、await
へアサインする前にPromise
に使いましょう!
// use then() after Promise, not variable
async function awaitPromiseChain() {
let data = await Promise.resolve('abc')
.then((result) => console.log(result));
}
awaitPromiseChain(); // abc
await
with reject (error handling)
async
関数実行中、await
がreject
されたPromise
を受けるとすぐasync
関数を中断する。
// await with reject
async function foo() {
await Promise.reject('something wrong'); // reject will suspend following execution
console.log('not work');
}
async function foo1() {
return 'it works';
}
foo().then((result) => console.log(result));
foo1().then((result) => console.log(result));
console.log('1');
// 1
// it works
// UnhandledPromiseRejection
確かにasync
関数は、普通の関数のように中断されるとキャッチされていないエラーがそのまま外側に投げプログラムが中断されることがないです。それにほかのasync
関数がいてもエラーに影響されず、未処理のエラーが最後一気に投げてくるだけです。
それでもエラーキャッチや処理が大事です。async
関数内では一つのawait
に限られていなく、多くの場合はステップバイステップのように前の処理が確実に完成したうえ、それからの操作を実行していく、一種の確認作業でもあります。
外側からエラーをキャッチするのがいいけれどあまり意味しません。(上のようにasync
関数はメインをブロッキングしない。)
なのでasync
関数内try...catch
でawait
を包んだり、await
にアサインする値がPromise
オブジェクトなら.catch()
でキャッチしてもいいです。
// error handling
async function foo() {
await Promise.reject('something wrong'); // reject will suspend following execution
console.log('not work');
}
foo()
.then((result) => console.log(result))
.catch((err) => console.log(err));
// something wrong
// better way
async function foo() {
// to prevent suspend, catch the error in function first
try {
await Promise.reject('something wrong');
} catch (err) {
console.log(err);
return await Promise.resolve('hello world');
}
}
foo()
.then((result) => console.log(result));
// something wrong
// hello world
// or
async function foo() {
await Promise.reject('something wrong')
.catch((err) => console.log(err));
return await Promise.resolve('hello world');
}
foo()
.then((result) => console.log(result));
// something wrong
// hello world
try...catch
には実行してほしいコード、もし予定通りに実行してなかったりエラーだったり場合はキャッチして出力。
下のようにtry...catch
のawait
にエラーになってもreturn
に置かれたawait
は実行してくれます。
// use try...catch
async function foo() {
// catch error, and return resolved Promise
try {
await new Promise((resolve, reject) => {
throw new Error('something wrong');
});
} catch (err) {
console.log(err);
}
// return 'hello world';
return await ('hello world'); // await has no effect in here
}
foo()
.then((result) => console.log(result))
.catch((err) => console.log(err));
// note: double check, if return value was reject, we can catch error here
// Error: something wrong
// hello world
外側に.catch()
を使うことで、もし返り値がエラーかreject
されたPromise
だった場合はキャッチしてくれます。
// multiple actions
async function multipleAwait() {
try {
const value1 = await firstStep();
const value2 = await secondStep(value1);
const value3 = await thirdStep(value1, value2);
console.log('Final: ', value3);
} catch (err) {
console.log(err);
}
}
複数のawait
からエラーによって特定する場合は、エラーメッセージから分類してからどう処理していくかを決めるのもいいと思います。
// retry with for Loop
const superagent = require('superagent');
const retryCounter = 3;
async function test() {
let i;
for (i = 0; i < retryCounter; ++i) {
try {
await superagent.get('http://google.com/this-throws-an-error');
break;
} catch (err) { }
}
console.log(i);
}
test(); // 3
thenable vs. Promise (Microtask)
thenable
オブジェクトというのはthen()
メソッド持ちのオブジェクトのことです。下は同じく参考文章から取った例です。
// await with thenable
class SleepTime {
constructor(timeout) {
this.timeout = timeout;
}
then(resolve, reject) {
const startTime = Date.now();
setTimeout(
() => resolve(Date.now() - startTime),
this.timeout
);
}
}
(async () => {
const sleepTime = await new SleepTime(999);
console.log(sleepTime);
})();
function sleep(interval) {
return new Promise((resolve) => {
setTimeout(
() => resolve(),
interval);
})
}
async function oneToFiveInAsync() {
for (let i = 1; i <= 5; i++) {
console.log(i);
await sleep(1000);
}
}
oneToFiveInAsync();
// 1
// 2
// 1007
// 3
// 4
// 5
出力結果にとても気になりますね。一番目のasync
関数はIIFE
なのになぜ二番目のasync
関数oneToFiveInAsync()
より遅くなるでしょうか。
前も少し触れていたが、非同期関数Promise
や非同期APIら(setTimeout/setInterval
など)は別々のキューに優先順で実行されるのです。
Promise
はMicrotask
に、setTimeout/setInterval
はMacrotask
にタスクを実行する。実行が終わったら各自のQueue
(キュー、隊列)に送られ、コールバックの実行を待ちます。
優先順としてはMicrotask Queue
は常にMacrotask Queue
より先、Macrotask Queue
の実行がMicrotask Queue
がクリアするまでしない。そしてMicrotask Queue
はCall stack
がクリアするまで実行しない。
Call stack > Microtask Queue > Macrotask Queue
Microtask Queue
は解決されたPromise
が順番通りに実行する隊列ですが、
Macrotask Queue
は多くはタイマー持ち非同期APIのコールバック隊列なので、順序には意味を持たない。
ここから上の例の実行順の説明です。
自分の解釈が正しいかはかわかりませんが、説明に試してみたいと思います。
(task
には番号づけて説明するが、task Queue
は当時の実行順で書いています。)
Call stack
:
IIFE
⇒new SleepTime(1000)
⇒setTimeout()
⇒
Macrotask (1) start, timer in IIEF is executing
(Is Call stack
clear? No.
Is Microtask Queue
clear? Yes.
Is Macrotask Queue
clear? Yes.)
↓
Call stack
:
oneToFiveInAsync()
⇒for Loop
(Is Call stack
clear? No.
Is Microtask Queue
clear? Yes.
Is Macrotask Queue
clear? Yes.)
↓
Call stack
:
for Loop (1/5) start
⇒console.log(1)
⇒sleep(1000)
⇒
new Promise() in sleep function
⇒ Microtask (1) start
⇒
setTimeout()
⇒Macrotask (2) start, timer in Promise is executing
(Is Call stack
clear? Yes.
Is Microtask Queue
clear? Yes.
Is Macrotask Queue
clear? Yes. )
↓ one second later
Macrotask (1) end, timer in IIEF had been executed
⇒Macrotask Queue (1)
Macrotask (2) end, timer in Promise had been executed
⇒Macrotask Queue (2)
(Is Call stack
clear? Yes.
Is Microtask Queue
clear? Yes.
Is Macrotask Queue
clear? No. )
↓
Macrotask Queue (1) start, setTimeout callback anonymous executed
⇒
resolve(Date.now() - startTime)
⇒ Microtask (2) start
(Is Call stack
clear? Yes.
Is Microtask Queue
clear? Yes.
Is Macrotask Queue
clear? No. )
↓
Macrotask Queue (2) start, setTimeout callback anonymous executed
⇒
resolve()
⇒ Microtask (1) end
⇒
return resolved Promise
⇒ Microtask Queue (1)
(Is Call stack
clear? Yes.
Is Microtask Queue
clear? No.
Is Macrotask Queue
clear? Yes. )
↓
Microtask Queue (1) start & end
⇒await sleep(1000)
⇒for Loop (1/5) done
(Asynchronous) Microtask (2) end
⇒ Microtask Queue (1)
(Is Call stack
clear? No.
Is Microtask Queue
clear? Yes.
Is Macrotask Queue
clear? Yes. )
↓ for loop is synchronous
Call stack
:
for Loop (2/5) start
(Is Call stack
clear? No.
Is Microtask Queue
clear? No.
Is Macrotask Queue
clear? Yes. )
↓
console.log(2)
⇒sleep(1000)
⇒
new Promise() in sleep function
⇒Microtask (3) start
⇒
setTimeout()
⇒Macrotask (3) start, timer in Promise is executing
↓
(Is Call stack
clear? Yes.
Is Microtask Queue
clear? No.
Is Macrotask Queue
clear? Yes. )
Microtask Queue (1) start & end
⇒
sleepTime = await new SleepTime(1000)
⇒console.log(sleepTime)
↓
(Is Call stack
clear? Yes.
Is Microtask Queue
clear? Yes.
Is Macrotask Queue
clear? Yes. )
↓ one second later
Macrotask (3) end, timer in Promise had been executed
⇒Macrotask Queue (1)
...
...とかなり自分勝手に解釈して書いてみました。
長くて読みづらいかもしれないが、ほかのスレッド/タスクキューとの相互作用と、レンダリングのタイミングや、非同期HTTPリクエストなども加えればもっと複雑になります。今の自分はとにかくこの三つの実行順に理解したいと思います。
同じ例で少し変更すれば全然違う結果が出てきました。
(async () => {
const sleepTime = await new SleepTime(0);
console.log(sleepTime);
})();
...
// 1
// 5 // sleepTime
// 2
// 3
// 4
// 5
出力順番が変わったというのは、new SleepTime(0)
がsleep(1000)
より先にタイマーが終わりコールバックからresolve()
によって解決されたPromise
がMicrotask Queue
隊列に入ってしまったわけです。
その理由を説明する前にまずthenable
中のsetTimeout()
とPromise
中のsetTimeout()
の動きを解釈してみたいと思います。
thenable
のnew SleepTime()
はsetTimeout(() => resolve(), ms)
、タイマーの後anonymous()
⇒resolve()
によりMicrotask (1)
が始まり、そして解決されたPromise
をMicrotask Queue
へ送る。
一方sleep(1000)
はnew Promise()
実行の時点でMicrotask (2)
が始まり、そのあとタイマーからanonymous()
⇒resolve()
の実行により解決されたPromise
をMicrotask Queue
へ送るのです。
これは一番目の例でも二番目の例でも同じタスク順に沿って動作するのです。
もう気づいているかもしれませんが、Microtask Queue
の順番はMicrotask
のタスク順とは関係なく、先に処理が終わる方がMicrotask Queue
に入り、そのあとcall stack
へ移行し実行するのがMicrotask Queue
の順番から一番古いやつを取り出し実行していくのです。
なおこの例ではnew SleepTime(0)
からnew SleepTime(995)
まで順番が変わらずsleepTime
がさきに出力されるのです。new SleepTime(999)
なら実行速度により順番が上がったり下がったりしています。
この二つの例からCall stack
、Microtask Queue
、Macrotask Queue
の検証によりタイマーが狂う理由、そして Promise
の非同期操作というのはMicrotask
にあること 、今回ではうまく説明できたらいいと思います。
ここまで来て少し変な言い方がするかもしれませんが、こんな検証や説明で自分の理解が本当に正しいのかって自分でも自分に問いてます。まだ分からない部分がたくさんあって、何度も試行錯誤を重ねて自分の考えやロジックにチャレンジし、資料をどれだけ調べても読んでもやはり分からないことがありすぎて分からないまま放置しているのがよくあります。
ただ、この部分が大事だと直感的に感じて手が勝手に動いて、問題意識しながらテストの間でもだんだん面白くなってきました。そして自分も今回の検証からこれまでPromise
のステータスやタスク、タスクキューへの認識があやふやだったところ、間違っている部分に気づいていましたので、何日もかけたけど自分のなかではとても有意義な時間でした。
下は同じ概念で簡潔化にしたコードです。
// thenable vs. promise
class Thenable {
then(resolve, reject) {
setTimeout(
() => resolve('I am Thenable Object'),
1000
)
}
}
async function thenable() {
let str = await new Thenable()
console.log(str)
}
//
function PromiseToSet() {
return new Promise((resolve, reject) => {
setTimeout(
() => resolve('I am Promise'),
1000
)
})
}
async function test() {
let str = await PromiseToSet();
console.log(str);
}
thenable();
test();
// I am Thenable Object
// I am Promise
タイマーへが非同期の動きを観察するコードです。
最後にもう少し自分なりにまとめてみたいと思います。
自分から見れば、JSの非同期操作/非同期処理の実現というのは、タスク順とは関係なくタスク各々の処理を実行するところです。しかしMicrotask Queue
隊列に入った実行待ちのコールバックは、実質上同期に動いています。
そうでないと、Call stack > Microtask Queue > Macrotask Queue
のような優先度づけたり、Microtask Queue
では順番通りに動く必要がありません。すべてがsingle-thread
のベースからなるプロセスだからと思います。
synchronous vs. asynchronous in async function
気になってしょうがないパートです。async function
は普通の関数と違い、非同期generator
に近い概念からなる関数というのがわかりますが、中身の動き、そしてawait
を加えたらどうなるのでしょうか?
まずはawait
からです。await
は右にある値が解決されたPromise
を期待するが、そうでない場合は一度Promise
でラッピングして非同期処理を行わせます。しかし(Microtask queue
から返した)結果を待たねばならないので下のコードが同期に動くしかありません。
(今から思い返せば、await
は大きな処理ならばある意味で中身をブロッキング?...するかもしれないけど、async
関数自体はmain()
の処理を妨げないように設計されていると思います。普通の関数とは一番の違いです。)
// synchronous in async function
function getFoo() {
return 'Foo'
}
function getBar() {
return 'Bar'
}
async function syncTest() {
let foo = await getFoo();
let bar = await getBar();
return [foo, bar];
}
syncTest().
then((result) => console.log(result));
// [ 'Foo', 'Bar' ]
非同期の実現はMicrotask
にあるので、Promise
で包んだら同時実行ができるようになります。
// asynchronous in async function
async function asyncTest1() {
let [foo, bar] = await Promise.all(
[getFoo(), getBar()]
);
return [foo, bar];
}
asyncTest1()
.then((result) => console.log(result));
// [ 'Foo', 'Bar' ]
解決されたPromise
がMicrotask queue
に移行しthen()
の中身の処理を実行していきます。for Loop
は同期に動くしかありません。
// Promise.allSettled(iterable)
async function stepByStep() {
// asynchronous
let data = Promise.allSettled(
[getFoo(), getBar(), Promise.reject('something wrong')]
);
// console.log(data instanceof Promise); // true
// asynchronous
data
.then((result) => {
// synchronous
for (let obj of result) {
if (obj.status === 'fulfilled') {
console.log(obj.value);
} else {
console.log(obj.reason);
}
}
});
}
stepByStep();
// Foo
// Bar
// something wrong
ここからresolved Promise
(resolve()
)とunsettled Promise
(Promise()
)の動きが気になって、
// asynchronous with synchronous
function test() {
// asynchronous => Promise.resolve(resolved Promise) => Microtask Queue(then(onFulfilled[, onRejected]))
// synchronous => then(onFulfilled()) => for Loop => Call stack
return Promise.resolve(['Foo', 'Bar']).then(
(result) => {
for (let i = 0; i < result.length; i++) {
console.log(result[i]);
}
}
);
}
test();
// Foo
// Bar
(ここはawait
使う必要がない、一貫性のためにasync
を取りました。)
注釈が長すぎて読みづらいと思う方がいるかもしれないので、非同期の概念を教えてくれた Philip Roberts 氏が作ったサイトを借りて、ヴィジュアル化したシミュレーションは下にあります。(async
キーワードがサポートされていませんが。)
function test() {
// asynchronous => Promise => Microtask, resolved Promise => Microtask queue(then())
// synchronous => then(onFulfilled()) => for Loop => Call stack
return new Promise((resolve) => {
resolve(['Foo', 'Bar'])
}).then((result) => {
for (let i = 0; i < result.length; i++) {
console.log(result[i])
}
});
}
test();
// Foo
// Bar
ここからまたawait
に戻ります。
非同期Promise
と非同期APIsetTimeout(callback, timer)
にawait
を加えたらどうなるでしょう。
// asynchronous with asynchronous
async function test() {
// asynchronous => Promise => Microtask, resolved Promise => Microtask queue
// asynchronous => setTimeout(callback, timer) => timer to Macrotask => callback to Macrotask queue =>
// callback => resolve => Microtask => resolve() => resolved Promise => Microtask queue
let str1 = new Promise((resolve) => {
setTimeout(() => resolve('first'), 2000);
});
let str2 = new Promise((resolve) => {
setTimeout(() => resolve('second'), 1000);
});
Promise.race([str1, str2]).then((result) => console.log(result));
}
test(); // second
//
// add "await"
// await make everything in async function acts synchronously
// because it needs to wait until Promise is resolved
async function test() {
let str1 = await new Promise((resolve) => {
setTimeout(() => resolve('first'), 2000);
});
let str2 = await new Promise((resolve) => {
setTimeout(() => resolve('second'), 1000);
});
Promise.race([str1, str2]).then((result) => console.log(result));
}
test(); // first
やはりawait
の待つ行為は関数内の動きを同調にしてしまいます。
でもこれだけでawait
を使うのが非同期の意味がなくなるなんて思わないです。逆に言うと何か準備を整えてから次のステップに行くときawait
を使うべきだと思います。それにawait
を通してエラーキャッチと処理、ブレークポイントとして使うのもありだと思います。そして最も大事なのは、await
を通して後ろの値が解決されたPromise
ではなくても、解決されたPromise
としてMicrotask Queue
に移行し、外側に何が同期/非同期に動作しているコードがあってもブロッキングしないことです。
// check: Is variable's problem? No.
async function test() {
Promise.race([
await new Promise((resolve) => {
setTimeout(() => resolve('first'), 2000);
}),
await new Promise((resolve) => {
setTimeout(() => resolve('second'), 1000);
})
]).then((result) => console.log(result));
}
test(); // first
//
async function test() {
Promise.race([
new Promise((resolve) => {
setTimeout(() => resolve('first'), 2000);
}),
new Promise((resolve) => {
setTimeout(() => resolve('second'), 1000);
})
]).then((result) => console.log(result));
}
test(); // second
ES2022 Top-level await
expression
○ ES6 module × CommonJs
ちょうど新しい使い方を見かけたので、一緒にまとめようと思います。
ES2022から最上位のawait
を追加しました。これまで非同期処理を使用するモジュールから一部の値をimport
するとundefined
になるリスクがあるということで、
// Problem
// awaiting.js
let output;
async function main() {
const dynamic = await import(someMission); // import another module
const data = await fetch(url);
output = someProcess(dynamic.default, data);
}
main();
export { output };
// note: if we import awaiting.js before actions in async function
// haven't completed, output will be undefined
// usage.js
import { output } from './awaiting.js';
function outputPlusValue(value) {
return output + value; // output could be undefined, it depends on loading
}
console.log(outputPlusValue(100));
最上位await
が利用できるまでは、Promise
としてexport
するのが解決策のようでした。
// alternative: export Promise
let output;
export default (async function main() {
const dynamic = await import(someMission);
const data = await fetch(url);
output = someProcess(dynamic.default, data);
})();
export { output };
// note: here export Promise, when we use output in another file,
// we can use await to ensure it is loaded
そしてこれが最新の書き方です。
// solution: top-level await
// awaiting.js
const dynamic = import(someMission);
const data = fetch(url);
export const output = someProcess((await dynamic).default, await data);
// note: use await to ensure modules are loaded
// usage.js
import { output } from '.awaiting.js';
function outputPlusValue(value) {
return output + value; // now, output always has value
}
console.log(outputPlusValue(100));
(実はこの二つ書いたことないです(汗)。一応メモとして残します!)