teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

5

追記部分への対応

2018/12/15 04:55

投稿

miyabi-sun
miyabi-sun

スコア21465

answer CHANGED
@@ -86,4 +86,130 @@
86
86
  console.log('IDが重複しています');
87
87
  }
88
88
  }
89
- ```
89
+ ```
90
+
91
+ ---
92
+
93
+ 【追記箇所への回答】
94
+
95
+ > 1.staticなメソッドをasyncさせる方法はないのでしょうか?
96
+
97
+ まぁ、参考書とかもないですし情報少なくて辛いですよね。
98
+ これを参考にしてみてください。
99
+
100
+ ※await構文の右辺がPromiseの場合、実行してresolve(value)のvalue部分を取り出しますが、String等の値であればそれをそのまま利用しようとします。
101
+
102
+ ```JavaScript
103
+ class Animal {
104
+ static async meow () {
105
+ return await 'meow!'
106
+ }
107
+ }
108
+ Animal.meow().then(console.log);
109
+ // "meow!"が出力される
110
+ ```
111
+
112
+ > 2.以下のvalidateRegisterUsersData()のようなPromiseチェーンを組むときに3変数になってしまうのですが、isFilledUsernameの引数をusernameだけにしてもPromiseチェーンは組めるのでしょうか?
113
+
114
+ できる or できないで言えばできます。
115
+ Promiseのreslve関数を実行するときの書式で、`resolve(1, 2, 3)`という風に3つの引数で実行した場合、
116
+ `.then((a, b, c) => console.log(a, b, c))`という風に3つの引数で受けることが可能です。
117
+
118
+ ただし、async / await構文で戻って来る場合、resolveの第一引数しか持って帰れません。
119
+ Promise返す関数は最小の挙動を記述すれば、そんなにたくさんの引数を持って帰りたいという要求は出るはずがありません。
120
+
121
+ > 3.以下の2点を守ってコードを書き換える際に、Promiseチェーンを組むときは以下のような形になると思ったのですが、間違ってますか?
122
+
123
+ そもそもの話で、Promiseが必要な関数/メソッドってどういうものだと思いますか?
124
+ それは「非同期処理であるべきもの」だけです。
125
+
126
+ 非同期処理であるべきものとは何でしょうか?
127
+ それは主に「HDDに問い合わせる」「ネットワーク越しに問い合わせる」といった、メモリに保存済みのものを呼び出すものより圧倒的に遅いものがその対象になるべきです。
128
+
129
+ > ```JavaScript
130
+ > static __isFilledPassword(password, isValidated, errors) {
131
+ > new Promise((resolve, reject) => {
132
+ > isValidated = false;
133
+ > errors.password = "passwordが未入力です。";
134
+ > });
135
+ > }
136
+ > ```
137
+
138
+ passwordが変数が空か否かを確認するメソッドは非同期処理であるべきか?
139
+ と聞かれれば、別にHDDやネットワーク越しに問い合わせるわけではないので不要です。
140
+
141
+ 要するにお前Promiseである必要ないじゃんって話ですし、
142
+ isFilledPasswordというメソッド/関数ならば、普通のエンジニアはtrue or falseが帰って来ることを想定するはずです。
143
+ この辺のメソッド名の名付けや挙動は英語力も必要になってきます。
144
+
145
+ Authenticatorクラスの中の話なので、例えばinvalidReasonsという配列が帰ってきそうなメソッド名にしてはいかが?
146
+ 配列が空、つまり`reasons.length === 0`がtrueならば妥当なデータ、falseならば不正な値というわけです。
147
+ これなら死ぬほど使いやすくなったと思いませんか?
148
+
149
+ ```JavaScript
150
+ class Authenticator {
151
+ static invalidReasons({username, password}) {
152
+ const reasons = [];
153
+ if (username === '') {
154
+ reasons.push('usernameが未入力です');
155
+ }
156
+ if (password === '') {
157
+ reasons.push('passwordが未入力です。');
158
+ }
159
+ return reasons;
160
+ }
161
+ }
162
+ ```
163
+
164
+ > `isValidated = false;`
165
+
166
+ 細かいですが、こういう使い方はバグの原因なので極力やめてください。
167
+ こういう使い方する関数を「副作用がある関数」と呼び、一定以上の技量のエンジニアには嫌われる使い方です。
168
+ パフォーマンス改善のテクニックの一つではあるのですが、これを意識せず使いまくったシステムだと単純にバグの量が100倍増えるでしょう、それだけのコストを払う意味があるケースは実際殆ど存在せず禁じ手になっています。
169
+
170
+ また、今回の用途では正しく動作しません。
171
+ 何故かというとString、Number、Booleanなどのプリミティブ値は値渡しなので、関数の内側で引数として宣言したisValidated変数の内容は変わりますが、引数として渡した外側の世界の変数には影響しません。
172
+
173
+ ```JavaScript
174
+ var add3 = it => {
175
+ it += 3;
176
+ return it;
177
+ }
178
+ var a = 1;
179
+ console.log(add3(a)); // 4
180
+ console.log(a); // 1
181
+ ```
182
+
183
+ オブジェクトや配列等のオブジェクト系はJavaScriptでは一見参照渡しのように思えますが、
184
+ 実際には参照の値渡しなので、やはり束縛が切れるので無意味です。
185
+
186
+ ```JavaScript
187
+ var nameChange = (obj, name) => {
188
+ obj = {name};
189
+ return obj;
190
+ }
191
+ var taro = {name: 'taro'}
192
+ var jiro = nameChange(taro, 'jiro')
193
+ console.log(taro); // {name: 'taro'}
194
+ console.log(jiro); // {name: 'jiro'}
195
+
196
+ // 副作用を起こしたければこうする
197
+ var nameChange2 = (obj, name) => {
198
+ obj.name = name; // 変数への代入ではなく、プロパティを書き換えるような挙動にする
199
+ return obj;
200
+ }
201
+ var taro = {name: 'taro'}
202
+ var jiro = nameChange2(taro, 'jiro')
203
+ console.log(taro); // {name: 'jiro'} <- 副作用の再現に成功
204
+ console.log(jiro); // {name: 'jiro'}
205
+ ```
206
+
207
+ > // __isFilledUsername = username, isValidated, errors => new Promise() だとエラー
208
+
209
+ ES2015のクラス構文の書き方を復習しましょう
210
+ [クラス - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes)
211
+
212
+ それと、アロー関数のルールで`username, isValidated, errors => new Promise()`のように引数が複数になる場合は引数の部分のカッコは省略できません。
213
+ アロー関数でカッコを省略できるのは引数が1個のケースだけです。
214
+
215
+ [アロー関数 - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions)

4

多分これで完成形

2018/12/15 04:55

投稿

miyabi-sun
miyabi-sun

スコア21465

answer CHANGED
@@ -59,7 +59,7 @@
59
59
 
60
60
  // 最初の1行目で返すなら{}もreturnも不要でこの様に書いたほうがいい
61
61
  // 関数は外のbodyみたいな値に依存するのではなく、引数で受け付けたほうがいい
62
- const isUniqueUsername = (username) => new Promise((resolve, reject) => {
62
+ const isUniqueUsername = username => new Promise((resolve, reject) => {
63
63
  // usernameが空か否かはisUniqueで確認するべきじゃないので、バリデート関数を別途用意すべき
64
64
 
65
65
  connection.query({
@@ -73,11 +73,7 @@
73
73
  }
74
74
 
75
75
  // 余計な事はせずにやること終わったらさっさとtrue or false返して終了する
76
- if (results.length === 0) {
76
+ resolve(results.length === 0);
77
- resolve(true);
78
- } else {
79
- resolve(false);
80
- }
81
77
  });
82
78
  });
83
79
 

3

更に修正

2018/12/14 09:44

投稿

miyabi-sun
miyabi-sun

スコア21465

answer CHANGED
@@ -60,26 +60,19 @@
60
60
  // 最初の1行目で返すなら{}もreturnも不要でこの様に書いたほうがいい
61
61
  // 関数は外のbodyみたいな値に依存するのではなく、引数で受け付けたほうがいい
62
62
  const isUniqueUsername = (username) => new Promise((resolve, reject) => {
63
- // 長いif文によるネスト可読性を下げるので、ド節でぐ返す
63
+ // usernameが空か否かisUniqueで確認すべきじゃないので、バリデト関数を別途用意べき
64
- if (!username) {
65
- // resolve(false)かreject(エラー理由)かは要検討
66
- resolve(false);
67
- return;
68
- }
69
- if (!password) {
70
- resolve(false);
71
- return;
72
- }
73
64
 
74
65
  connection.query({
75
66
  sql: "SELECT * FROM users WHERE username = ?;",
76
67
  values: [username]
77
68
  }, (error, results, fields) => {
78
- // 余計な事せずにること終わったさっさとtrue or false返して終了する
69
+ // errorがnull以外の時、MySQLのコネクション失敗やらの申告なエラーなのでrejectで対応
79
70
  if (error) {
80
71
  reject(error);
81
72
  return;
82
73
  }
74
+
75
+ // 余計な事はせずにやること終わったらさっさとtrue or false返して終了する
83
76
  if (results.length === 0) {
84
77
  resolve(true);
85
78
  } else {

2

回答のコードがちょっと変だったので修正

2018/12/14 09:42

投稿

miyabi-sun
miyabi-sun

スコア21465

answer CHANGED
@@ -59,7 +59,7 @@
59
59
 
60
60
  // 最初の1行目で返すなら{}もreturnも不要でこの様に書いたほうがいい
61
61
  // 関数は外のbodyみたいな値に依存するのではなく、引数で受け付けたほうがいい
62
- const isUsernameUnique = ({username, password}) => new Promise((resolve, reject) => {
62
+ const isUniqueUsername = (username) => new Promise((resolve, reject) => {
63
63
  // 長いif文によるネストは可読性を下げるので、ガード節ですぐ返す
64
64
  if (!username) {
65
65
  // resolve(false)かreject(エラー理由)かは要検討
@@ -73,7 +73,7 @@
73
73
 
74
74
  connection.query({
75
75
  sql: "SELECT * FROM users WHERE username = ?;",
76
- values: [body.username]
76
+ values: [username]
77
77
  }, (error, results, fields) => {
78
78
  // 余計な事はせずにやること終わったらさっさとtrue or false返して終了する
79
79
  if (error) {
@@ -90,8 +90,8 @@
90
90
 
91
91
  const main = async () => {
92
92
  // bodyをどっから取ってくるかは知らんけどこんな感じ
93
- var isValidated = await isUsernameUnique(body);
93
+ var isUnique = await isUniqueUsername(body.username);
94
- if (isValidated) {
94
+ if (isUnique) {
95
95
  console.log('ユニークなIDです');
96
96
  } else {
97
97
  console.log('IDが重複しています');

1

><

2018/12/14 09:39

投稿

miyabi-sun
miyabi-sun

スコア21465

answer CHANGED
@@ -42,6 +42,8 @@
42
42
  実際にこれを走らせるとthenの意味はまるでなくて、その場で全てのPromiseが同時に走るわ、引き継がせるデータがちゃんとリレー出来てないわという有様で、
43
43
  意図したように動かないんじゃないの?という印象ですね。
44
44
 
45
+ 特に設計思想としてはpromise走らせた結果として`resolve(isValided)`を実行しながら返すという設計にすべきで、予め`let isValided = true;`を実行しておいて、Promiseの中身で外のスコープの変数を書き換えてfalseにするのはお前Promiseの意味ないじゃんということで落第です。
46
+
45
47
  > どうすれば順番に処理されるのでしょうか?
46
48
 
47
49
  Promise覚え直しですね。