Skip to content

Commit de5f820

Browse files
committed
write(column-promise-resolve): 同期と非同期の混在の問題についてを追加
  resolve #167
1 parent ea8ca5d commit de5f820

File tree

7 files changed

+109
-2
lines changed

7 files changed

+109
-2
lines changed

Ch2_HowToWrite/column-promise-resolve.adoc

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,68 @@ JavaScriptは上から実行されていくため、まず最初に`<1>`が実
3737
プログラム的には同期的にコールバック関数に`42`を渡して呼び出す事はできますね。
3838

3939
しかし、Promiseでは`promise.then`の時点でpromiseの状態が決まっていても、
40-
そこで登録したコールバック関数は非同期で呼び出されることが定義されています
40+
そこで登録したコールバック関数は非同期で呼び出される仕様になっています
4141

4242
それは何故でしょうか?
4343

44-
=== 同期と非同期の混在
44+
=== 同期と非同期の混在の問題
45+
46+
もう少し一般的な問題として考えみましょう。
47+
48+
これは、コールバック関数を受け取る関数が、
49+
状況によって同期処理になるのか非同期処理になるのかが変わるのと同じ問題です。
50+
51+
次のような、`onReady(fn)` 関数を見てみましょう。
52+
53+
[role="executable"]
54+
[source,javascript]
55+
[[mixed-onready.js]]
56+
.mixed-onready.js
57+
----
58+
include::embed/embed-mixed-onready.js[]
59+
----
60+
61+
これは、`onReady(fn)`が呼ばれるまでにDOMが読み終わってるかで、
62+
コンソールに出てくるメッセージの順番が変わってしまいます。
63+
64+
この問題の対処として常に非同期で呼び出すようにすることです。
65+
66+
[role="executable"]
67+
[source,javascript]
68+
[[async-onready.js]]
69+
.async-onready.js
70+
----
71+
include::embed/embed-async-onready.js[]
72+
----
73+
74+
この問題については、 http://effectivejs.com/[Effective JavaScript] の
75+
**項目67 非同期コールバックを同期的に呼び出してはいけない** で紹介されています。
76+
77+
[quote, David Herman, Effective JavaScript]
78+
____
79+
- 非同期コールバックは(たとえデータが即座に利用できても)決して同期的に使ってはならない。
80+
- 非同期コールバックを同期的に呼び出すと、処理の期待されたシーケンスが乱され、
81+
コードの実行順序に予期しない変動がしょうじるかもしれない。
82+
- 非同期コールバックを同期的に呼び出すと、スタックオーバーフローや例外処理の間違いが発生するかもしれない。
83+
- 非同期コールバックを次回に実行されるようスケジューリングするには、`setTimeout`のような非同期APIを使う。
84+
____
85+
86+
先ほどの`promise.then`も同様のケースであり、この同期と非同期処理の混在の問題がおきないようにするため、
87+
**Promiseは常に非同期** で処理されるという事が仕様で定められているわけです。
88+
89+
最後に、この`onReady`をPromiseを使って定義すると以下のようになります。
90+
91+
[role="executable"]
92+
[source,javascript]
93+
[[onready-as-promise.js]]
94+
.onready-as-promise.js
95+
----
96+
include::embed/embed-onready-as-promise.js[]
97+
----
98+
99+
Promiseは常に非同期で実行されることが保証されているため、
100+
`setTimeout`のような明示的に非同期処理をするコードがないことがわかると思います。
101+
45102

46103
////
47104
つまり、先ほどのコードは擬似的には以下のように展開出来ます。
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"use strict";
2+
function onReadyPromise() {
3+
return new Promise(function (resolve, reject) {
4+
var readyState = document.readyState;
5+
if (readyState == "interactive" || readyState === 'complete') {
6+
resolve();
7+
} else {
8+
window.addEventListener("DOMContentLoaded", resolve);
9+
}
10+
});
11+
}
12+
module.exports.onReadyPromise = onReadyPromise;

Ch2_HowToWrite/lib/onReady.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"use strict";
2+
function onReady(fn) {
3+
var readyState = document.readyState;
4+
if (readyState == "interactive" || readyState === 'complete') {
5+
fn();
6+
} else {
7+
window.addEventListener("DOMContentLoaded", fn);
8+
}
9+
}
10+
module.exports.onReady = onReady;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"use strict";
2+
function onReady(fn) {
3+
var readyState = document.readyState;
4+
if (readyState == "interactive" || readyState === 'complete') {
5+
setTimeout(fn, 0);
6+
} else {
7+
window.addEventListener("DOMContentLoaded", fn);
8+
}
9+
}
10+
module.exports.onReady = onReady;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"use strict";
2+
var onReady = require("../../lib/strictOnReady").onReady;
3+
onReady(function () {
4+
console.log("DOM fully loaded and parsed");
5+
});
6+
console.log("==Starting==");
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"use strict";
2+
var onReady = require("../../lib/onReady").onReady;
3+
onReady(function () {
4+
console.log("DOM fully loaded and parsed");
5+
});
6+
console.log("==Starting==");
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
"use strict";
2+
var onReadyPromise = require("../../lib/onReady-promise").onReadyPromise;
3+
onReadyPromise().then(function () {
4+
console.log("DOM fully loaded and parsed");
5+
});
6+
console.log("==Starting==");

0 commit comments

Comments
 (0)