回答編集履歴
7
最終更新
test
CHANGED
@@ -66,7 +66,15 @@
|
|
66
66
|
|
67
67
|
まずコールバック引数というのは1つ以上の引数を必ず参照する作りにしてください。
|
68
68
|
|
69
|
-
中身でエラーが出てもtry~catchで取る事が出来ないので、
|
69
|
+
中身でエラーが出てもtry~catchで取る事が出来ないので、
|
70
|
+
|
71
|
+
非同期処理を司るライブラリなんかは全て第一引数はかならずerrにするというルールがあるからです。
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
もし後続の処理を行われたらまずいという場合は、
|
76
|
+
|
77
|
+
`new Error('エラー理由')`でエラーを作成して第一引数に設定してください。
|
70
78
|
|
71
79
|
|
72
80
|
|
@@ -96,22 +104,26 @@
|
|
96
104
|
|
97
105
|
|
98
106
|
|
99
|
-
// コールバック関数は必ず第一引数をerrに
|
107
|
+
// コールバック関数は必ず第一引数をerrにする
|
100
108
|
|
101
|
-
// objとnewObjが使いた
|
109
|
+
// objとnewObjが使いたさそうでしたので両方要求する関数に
|
102
110
|
|
103
111
|
function asyncCallback (err, obj, newObj) {
|
104
112
|
|
105
113
|
if (err) console.error(err);
|
106
114
|
|
115
|
+
// 本命の処理を行う
|
116
|
+
|
117
|
+
console.log(obj);
|
118
|
+
|
107
|
-
console.log(
|
119
|
+
console.log(newObj);
|
108
120
|
|
109
121
|
}
|
110
122
|
|
111
123
|
|
112
124
|
|
113
|
-
|
125
|
+
// objもnewObjもここで宣言しない方が良いので削除
|
114
126
|
|
115
|
-
|
127
|
+
makeNewObj({}, asyncCallback);
|
116
128
|
|
117
129
|
```
|
6
再調整
test
CHANGED
@@ -64,7 +64,15 @@
|
|
64
64
|
|
65
65
|
|
66
66
|
|
67
|
-
コールバック引数というのは1つ以上の引数を必ず参照する作りにしてください。
|
67
|
+
まずコールバック引数というのは1つ以上の引数を必ず参照する作りにしてください。
|
68
|
+
|
69
|
+
中身でエラーが出てもtry~catchで取る事が出来ないので、第一引数はかならずerrになるからです。
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
そして第二引数以降に、コールバック関数が使いたいと考えている変数を全て引数としてぶち込んで下さい。
|
74
|
+
|
75
|
+
これはnewObjをasyncCallbackで使えるようにした例です。
|
68
76
|
|
69
77
|
|
70
78
|
|
@@ -80,7 +88,7 @@
|
|
80
88
|
|
81
89
|
// コールバックに値を引き継ぎたいので関数適用時の引数として渡す
|
82
90
|
|
83
|
-
callback(null, {new: true});
|
91
|
+
callback(null, obj, {new: true});
|
84
92
|
|
85
93
|
}, 0);
|
86
94
|
|
@@ -90,11 +98,13 @@
|
|
90
98
|
|
91
99
|
// コールバック関数は必ず第一引数をerrにしてください。
|
92
100
|
|
93
|
-
//
|
101
|
+
// objとnewObjが使いたいと思ってそうでしたので両方要求する関数にしました。
|
94
102
|
|
95
|
-
function asyncCallback (err, newObj) {
|
103
|
+
function asyncCallback (err, obj, newObj) {
|
96
104
|
|
105
|
+
if (err) console.error(err);
|
106
|
+
|
97
|
-
console.log(err, newObj)
|
107
|
+
console.log(err, newObj);
|
98
108
|
|
99
109
|
}
|
100
110
|
|
5
returnを削除出来て居なかったので再修正
test
CHANGED
@@ -84,19 +84,17 @@
|
|
84
84
|
|
85
85
|
}, 0);
|
86
86
|
|
87
|
-
// 新しいオブジェクトを返します。
|
88
|
-
|
89
|
-
return {new: true};
|
90
|
-
|
91
87
|
}
|
92
88
|
|
93
89
|
|
94
90
|
|
95
91
|
// コールバック関数は必ず第一引数をerrにしてください。
|
96
92
|
|
97
|
-
f
|
93
|
+
// if: もしobjも使いたければ引数を3つ取る関数にする
|
98
94
|
|
95
|
+
function asyncCallback (err, newObj) {
|
96
|
+
|
99
|
-
console.log(err,
|
97
|
+
console.log(err, newObj)
|
100
98
|
|
101
99
|
}
|
102
100
|
|
4
全面的に書き直し
test
CHANGED
@@ -1,32 +1,90 @@
|
|
1
|
+
> 以下はそのコードを質問用に簡略化したコードなのですが、関数式の代入先オブジェクトを関数に渡すコールバック関数の中で参照出来るのは、どういう仕組みからなのでしょうか?(質問1)
|
2
|
+
|
3
|
+
|
4
|
+
|
1
|
-
|
5
|
+
コールバック関数適用と戻り値は完全に異なるものです。
|
6
|
+
|
7
|
+
非同期処理でコールバック関数を渡したからといって、戻り値を返してはならないというルールは存在しません。
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
setTimeoutなんかはこの両者の違いを上手に使っている典型例の一つで、
|
12
|
+
|
13
|
+
一度コールバック関数で遅延させると同時に、IDが戻り値として返ってきます。
|
14
|
+
|
15
|
+
やっぱり実行を辞めたい場合はキャンセル関数に渡して上げる事で実行を取り下げる事が可能です。
|
2
16
|
|
3
17
|
|
4
18
|
|
5
19
|
```JavaScript
|
6
20
|
|
7
|
-
|
21
|
+
var id = setTimeout(() => console.log('test'), 10000);
|
8
22
|
|
9
|
-
|
23
|
+
// 異常を感じたのでやっぱりキャンセルだ!
|
10
24
|
|
11
|
-
|
25
|
+
clearTimeout(id);
|
12
26
|
|
13
|
-
|
27
|
+
// test <- キャンセルされたので文字は表示されない
|
14
28
|
|
15
|
-
process.nextTick(() => callback(err, value));
|
16
|
-
|
17
|
-
|
29
|
+
```
|
18
30
|
|
19
31
|
|
32
|
+
|
33
|
+
> とりあえず僕的にはこういうことが可能らしいということはわかったのですが、直感的ではないと感じていて、これは良い作法なのかどうかも気になります。(質問2)
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
setTimeoutのように意味があれば良い作法ですし、
|
38
|
+
|
39
|
+
意味が無ければ辞めたほうが良いでしょう。
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
[UNIX哲学](https://ja.wikipedia.org/wiki/UNIX%E5%93%B2%E5%AD%A6)に「一つのことを行い、またそれをうまくやるプログラムを書け。」とあります。
|
44
|
+
|
45
|
+
あまり色々な機能をゴテゴテと付け足した関数は汎用性が失われ使い勝手が悪くなります。
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
これらの事から、setTimeoutのようにキャンセルすることを見越してキー情報を返す。
|
50
|
+
|
51
|
+
…くらいの使い方以外はすることないんじゃないですかね?
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
> もし、これが良い作法ではない場合、このようにコールバックの中で代入先を参照したいような場合に、どのような方法が取れるのかを教えて頂けると嬉しいです。(質問3)
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
???
|
60
|
+
|
61
|
+
ああ、なるほど…
|
62
|
+
|
63
|
+
やっぱり使い方分かってないじゃないですか。
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
コールバック引数というのは1つ以上の引数を必ず参照する作りにしてください。
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
```JavaScript
|
20
72
|
|
21
73
|
function makeNewObj(obj, callback) {
|
22
74
|
|
23
75
|
// いろいろな処理がこの辺に
|
24
76
|
|
25
|
-
ot
|
77
|
+
// 非同期コールバックを表現するためにsetTimeoutを利用
|
26
78
|
|
79
|
+
setTimeout(function() {
|
27
80
|
|
81
|
+
// コールバックに値を引き継ぎたいので関数適用時の引数として渡す
|
28
82
|
|
83
|
+
callback(null, {new: true});
|
84
|
+
|
85
|
+
}, 0);
|
86
|
+
|
29
|
-
//
|
87
|
+
// 新しいオブジェクトを返します。
|
30
88
|
|
31
89
|
return {new: true};
|
32
90
|
|
@@ -34,132 +92,18 @@
|
|
34
92
|
|
35
93
|
|
36
94
|
|
95
|
+
// コールバック関数は必ず第一引数をerrにしてください。
|
96
|
+
|
37
97
|
function asyncCallback (err, value) {
|
38
98
|
|
39
|
-
// err, valueはこの関数内でアクセスすることになる
|
40
|
-
|
41
|
-
console.log(err, value)
|
99
|
+
console.log(err, value)
|
42
100
|
|
43
101
|
}
|
44
102
|
|
45
103
|
|
46
104
|
|
105
|
+
const obj = {};
|
106
|
+
|
47
107
|
const newObj = makeNewObj(obj, asyncCallback);
|
48
108
|
|
49
|
-
// これはあくまでmakeNewObjectの戻り値
|
50
|
-
|
51
|
-
// errやvalueにはどう足掻いてもたどり着けない。
|
52
|
-
|
53
|
-
// { new: true }
|
54
|
-
|
55
109
|
```
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
こんな関係になっています。
|
60
|
-
|
61
|
-
従って、ライブラリの先では「はいはい実行しておくよ、そのまま次に行ってね」と通信を開始するだけです。
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
なので、上記のerrやvalueを同期的なコードで受け取る事は絶対不可能です。
|
66
|
-
|
67
|
-
(ES2017のasync / awaitを活用すれば見てくれだけは同期的に出来ますけどね)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
---
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
コールバック関数というのは、子供のお使いみたいなものです。
|
76
|
-
|
77
|
-
では、朝子供が学校にいったママの行動を例としながら説明していきましょう。
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
まず朝子供に朝食を食べさせ学校に向かわせます。
|
82
|
-
|
83
|
-
ここまで子供は自宅に居るのでコントロール可能です(同期処理)
|
84
|
-
|
85
|
-
ママは子供を学校に送る関数を叩き、子供は自分の手元を離れて学校へ移動しはじめました。
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
子供が学校に行っている間に、ママは書き置きを作ります。
|
90
|
-
|
91
|
-
「学校から帰ってきたら、プリントを机に出す、手を洗う、冷蔵庫のプリンを食べる」
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
子供が学校から返ってきたら、その書き置きを読み、
|
96
|
-
|
97
|
-
自発的にプリント机に出し、手を洗って、冷蔵庫のプリンを取り出し始めます。
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
この書き置きがJavaScriptにおけるコールバック関数なのです。
|
102
|
-
|
103
|
-
子供が学校に行っている間、ママは学校で何があったかを把握することが出来ません。
|
104
|
-
|
105
|
-
なので、学校へ送り出す関数の戻り値で、先生が渡したプリントを取得することは出来ません。
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
このイメージをコードにあらわしてみました。
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
```JavaScript
|
114
|
-
|
115
|
-
function toSchool (child, kakioki) {
|
116
|
-
|
117
|
-
setTimeout(function () {
|
118
|
-
|
119
|
-
var print = '今日の学級通信';
|
120
|
-
|
121
|
-
kakioki(null, print);
|
122
|
-
|
123
|
-
}, 8 * 60 * 60 * 1000);
|
124
|
-
|
125
|
-
return child + 'が登校しました';
|
126
|
-
|
127
|
-
}
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
var child = '太郎';
|
132
|
-
|
133
|
-
var result = toSchool(child, function (err, print) {
|
134
|
-
|
135
|
-
// 慣習としてコールバック関数の第一引数はエラー
|
136
|
-
|
137
|
-
if (err) {
|
138
|
-
|
139
|
-
// 異常あり!子供が返ってこない!?電話する等の対応
|
140
|
-
|
141
|
-
throw err;
|
142
|
-
|
143
|
-
}
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
// 以降は書き置きの内容
|
148
|
-
|
149
|
-
console.log('プリントを机の上に出す');
|
150
|
-
|
151
|
-
console.log(print); // 今日の学級通信
|
152
|
-
|
153
|
-
console.log('手洗い');
|
154
|
-
|
155
|
-
console.log('冷蔵庫のプリンを食べる');
|
156
|
-
|
157
|
-
});
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
console.log(result);
|
162
|
-
|
163
|
-
// 太郎が登校しました
|
164
|
-
|
165
|
-
```
|
3
コードをもうちょっと洗練
test
CHANGED
@@ -128,23 +128,35 @@
|
|
128
128
|
|
129
129
|
|
130
130
|
|
131
|
-
|
131
|
+
var child = '太郎';
|
132
132
|
|
133
|
-
function
|
133
|
+
var result = toSchool(child, function (err, print) {
|
134
134
|
|
135
|
-
|
135
|
+
// 慣習としてコールバック関数の第一引数はエラー
|
136
136
|
|
137
|
-
|
137
|
+
if (err) {
|
138
138
|
|
139
|
-
//
|
139
|
+
// 異常あり!子供が返ってこない!?電話する等の対応
|
140
140
|
|
141
|
+
throw err;
|
142
|
+
|
141
|
-
}
|
143
|
+
}
|
142
144
|
|
143
145
|
|
144
146
|
|
145
|
-
|
147
|
+
// 以降は書き置きの内容
|
146
148
|
|
147
|
-
|
149
|
+
console.log('プリントを机の上に出す');
|
150
|
+
|
151
|
+
console.log(print); // 今日の学級通信
|
152
|
+
|
153
|
+
console.log('手洗い');
|
154
|
+
|
155
|
+
console.log('冷蔵庫のプリンを食べる');
|
156
|
+
|
157
|
+
});
|
158
|
+
|
159
|
+
|
148
160
|
|
149
161
|
console.log(result);
|
150
162
|
|
2
関係をもう少し詳しく
test
CHANGED
@@ -103,3 +103,51 @@
|
|
103
103
|
子供が学校に行っている間、ママは学校で何があったかを把握することが出来ません。
|
104
104
|
|
105
105
|
なので、学校へ送り出す関数の戻り値で、先生が渡したプリントを取得することは出来ません。
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
このイメージをコードにあらわしてみました。
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
```JavaScript
|
114
|
+
|
115
|
+
function toSchool (child, kakioki) {
|
116
|
+
|
117
|
+
setTimeout(function () {
|
118
|
+
|
119
|
+
var print = '今日の学級通信';
|
120
|
+
|
121
|
+
kakioki(null, print);
|
122
|
+
|
123
|
+
}, 8 * 60 * 60 * 1000);
|
124
|
+
|
125
|
+
return child + 'が登校しました';
|
126
|
+
|
127
|
+
}
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
// 約8時間経った後に実行
|
132
|
+
|
133
|
+
function kakioki (err, print) {
|
134
|
+
|
135
|
+
if (err) throw err; // 異常あり!子供が返ってこない!?電話する等の対応
|
136
|
+
|
137
|
+
console.log(print);
|
138
|
+
|
139
|
+
// 今日の学級通信
|
140
|
+
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
|
145
|
+
var child = '太郎';
|
146
|
+
|
147
|
+
var result = toSchool(child, kakioki);
|
148
|
+
|
149
|
+
console.log(result);
|
150
|
+
|
151
|
+
// 太郎が登校しました
|
152
|
+
|
153
|
+
```
|
1
process.nextTickを追加
test
CHANGED
@@ -10,7 +10,9 @@
|
|
10
10
|
|
11
11
|
// ネットワークやI/O通信が終わり次第、イベントループに設定されて下記のコードが実行される
|
12
12
|
|
13
|
+
// まぁ今回は横着してコンマ0秒で通信完了したことにしてprocess.nextTickを利用
|
14
|
+
|
13
|
-
callback(err, value);
|
15
|
+
process.nextTick(() => callback(err, value));
|
14
16
|
|
15
17
|
}
|
16
18
|
|