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