前置き
これについて学ぶ発端となったのは、以下のコードを書いているときでした。
リクエストを並列に走らせたく、Promise.allを使う必要がありました。
Promise.allの引数には、配列でPromiseオブジェクトを渡す必要があるので、下のputs()というPromiseオブジェクトを返す関数も作成しました。
以下ではエラーが出るのですが、どこが間違いか気づきますか?
method(A, B) {
// 処理
}
puts(A, B): Promise<void> {
return new Promise(function(resolve) => {
this.$api
.get('/tech-talk')
.then(response => {
this.method(A, B)
})
.then(() => resolve())
})
}
funcA(): Promise<void> {
const promises = [
this.puts(1, 1),
this.puts(1, 2),
this.puts(2, 1),
this.puts(2, 2)
]
Promise.all(promises).then(function() {
this.funcB()
})
}
間違いは以下になります。
puts(A, B): Promise<void> {
return new Promise(function(resolve) => {
// 'this' implicitly has type 'any' because it does not have a type annotation
this.$api
.get('/tech-talk')
.then(response => {
// 'this' implicitly has type 'any' because it does not have a type annotation
this.method(A, B)
})
.then(() => resolve())
})
}
functionA(): Promise<void> {
const promises = [
this.puts(1, 1),
this.puts(1, 2),
this.puts(2, 1),
this.puts(2, 2)
]
Promise.all(promises).then(function() {
// 'this' implicitly has type 'any' because it does not have a type annotation
this.functionB()
})
}
エラーメッセージが出ているのは、全て「this」を扱っている箇所です。
最初なぜなのか分かりませんでした。
正解パターンは以下です。
puts(A, B): Promise<void> {
return new Promise(resolve => {
this.$api
.get('/tech-talk')
.then(response => {
this.method(A, B)
})
.then(() => resolve())
})
}
functionA(): Promise<void> {
const promises = [
this.puts(1, 1),
this.puts(1, 2),
this.puts(2, 1),
this.puts(2, 2)
]
Promise.all(promises).then(() => this.functionB())
}
Promiseのコールバック関数を無名関数からアロー関数に変更しただけです。
無名関数とアロー関数のthisの取り扱い方の違い
関数 | thisの扱い方 |
---|---|
無名関数 | それが何を指し示すかは文脈によって変化する。 そのメソッドにアクセスしたオブジェクトが、thisとなる。 |
アロー関数 | 文脈に依存せず、宣言された時点でthisを確定する。 呼び出された場所をthisとする |
上に掲載したコードを例にすると、コールバック関数が無名関数の場合、thisはPromiseオブジェクト自身にあたるので、外のdataやmethodを参照できない。
逆にアロー関数の場合、呼び出し元からthisのスコープが引き継がれるので、外のdataやmethodも参照ができる。
補足
function が基準スコープになるのが JavaScript の大きな特徴 (var のスコープもこれに準ずる)。
そのため、function を入れ子にした途端 this の指すものが変わってきてしまうので注意が必要。
それに対して、アロー関数を利用すると関数をどんなに入れ子にしてもthisの指すものは変わらず固定化される。
その他無名関数とアロー関数の違い
JavaScript: 通常の関数とアロー関数の違いは「書き方だけ」ではない。異なる性質が10個ほどある。
参考記事
【Vue.js】Promiseのcallback処理にComponentで定義したmethodの使用
JavaScript の this を理解する多分一番分かりやすい説明