Promise
Promiseを使うときに、通常にnewしてから使います。
つまり、Promiseはコンストラクターです。では、console.dir(Promise)でPromiseの中身を見てみましょう!
Promiseの中にある、resolve, reject, catch, thenメソッドについてまだ分からないと思いますが、順を追って説明いたします。
◉また余談ですが、Promiseは英語で約束の意味があります。一定時間後に必ず結果(result)を返してくるねという風に覚えてればいいと思います。
まずPromiseの重要な属性、3つの状態から説明いたします。
とても大事な内容となるので、ぜひ理解してください。let a1 = new Promise((resolve)=>{
setTimeout(()=>{
resolve(1)
},2000);
})
console.log(a1);
consoleから確認できるように、2秒後Promiseの状態(PromiseState属性)がpendingからfullfilledに変わりました。
pending, fullfilled, rejectedはそれぞれ下記の意味を表しています。
● pending: 初期状態
● fullfilled: 処理が成功しました
● rejected: 処理が失敗しました
次に、Promise関数定義の仕方を見ていきましょう。
1のアロー関数の書き方が現在主流になっています。
Promiseの関数定義
// 書き方1
let a1 = new Promise((resolve, reject)=>{
})
// 書き方2
let a2 = new Promise(function(resolve, reject){
})
resolveとrejectメソッドは引数となります。他の名前にすることもできますが、一般的にそのままresolveとrejectを使うのが普通です。また他のプログラミング言語では、funciton(メソッド)を引数として渡せない場合が多いですが、javascriptができます。これもjavasript言語の魅力の一つです!
そのために、functionブロックにあるresolve(1)とはresloveメソッドに引数1を渡す意味となります。そして、もう一点覚えてほしいことは、Promiseの状態が一旦変われば、再度変更されることはありません。
例えば、pendingからfullfilledにchangeしたら、またrejectの状態に遷移することはありません。
下記の関数では、resolveメソッドが実行されると、rejectメソッドは実行しません。
resove, reject両者の中で一つのみしか実行されません。
let a1 = new Promise((resolve, reject)=>{
resolve();
reject(); // 実行されません
})
console.log(a1);
下記関数の実行結果を見てみましょう。
let a1 = new Promise((resolve, reject)=>{
resolve(1);
reject();
})
console.log(a1);
結果から分かるように、PromiseResultの結果は1です。では、どのようにすればPromiseResultの値を取得することができるのでしょうか。
答えはthenメソッドとcatchメソッドを使うことです。thenとcatch二つともcallback関数です。
● resolve関数の処理が成功されると、PromiseResultがthenメソッドに渡されます。
● reject関数の処理が成功されると、PromiseResultがcatchメソッドに渡されます。
new Promise((resolve,reject)=>{
resolve(123)
}).then(res=>console.log(res + '成功したら実行される'))
.catch(error=>console.log(err + '失敗したら実行される'))
.finally(()=>console.log('成功や失敗のどちらでも実行される'));
また、catchを省略し、下記のように書くこともできます。then関数に二つの引数を渡すという書き方です。
new Promise((resolve,reject)=>{
reject(123)
}).then((res)=>console.log(res + '成功'),
(err)=>console.log(err + '失敗'));
catch, then, resolve, rejectについて紹介はここまでにしたいと思います。次にPromise chainの概念について書きます。
まず前提として分かってほしいのは.then関数が処理された後の戻り値は新しいPromise対象です。
let a1 = new Promise((resolve,reject)=>{
resolve('123')
});
let a2 = a1.then((res)=>{});
console.log(a2); //undfined
console.log(a1 === a2); // false
処理結果はこんな感じです!
a1.thenの処理結果はundefinedなのに、なんでPromiseStateはfullfilledと疑問に思う方はいると思いますが、原因としては、reject関数が呼ばれていないため、PromiseStateはrejectに変更しません。resove関数について、例え結果はundefinedでもPromiseStateはfullfilledです。
例えば、下記の例だとPromiseStateはrejectになります。
let a1 = new Promise((resolve,reject)=>{
resolve('123')
});
let a2 = a1.then(()=>{
let a3 = new Promise((resolve,reject)=>{
reject()
})
return a3
})
console.log(a2);
では、Promise chainの使い方を見ていきましょう。
let a1 = new Promise((resolve, reject)=>{
resolve(1)
})
let a2 = a1.then((res)=>{
console.log(res); //1
return 2
})
let a3 = a2.then((res)=>{
console.log(res); //2
return 3
})
let a4 = a3.then((res)=>{
console.log(res); //3
})
もちろん変数名を省略することもできます。
let a1 = new Promise((resolve, reject)=>{
resolve(1)
}).then((res)=>{
console.log(res); //1
return 2
}).then((res)=>{
console.log(res); //2
return 3
}).then((res)=>{
console.log(res); //3
})
では、Promiseを使うことで、実際にどんな問題が解決できるのでしょうか。
はい、答えはcallbackの無限ループ問題を解決できます。
下記のコードを見ていきましょう。
function fn(a){
setTimeout(()=>{
console.log(a);
a--;
setTimeout(()=>{
console.log(a);
a--;
setTimeout(()=>{
console.log(a);
a--;
setTimeout(()=>{
console.log(a);
a--;
setTimeout(()=>{
console.log(a);
a--;
},1000)
},1000)
},1000)
},1000)
},1000)
}
fn(6);
コードから分かるように、毎回前回処理結果の値を使って新規の処理を行いますが、ソードコードがとても読みづらく、保守性も低いです。
Promiseを使えば、ソースコードは下記に変身できます!
function fn(a) {
new Promise((resolve,reject) =>{
resolve(a)
})
.then((params)=>{
return new Promise((resolve, reject)=>{
setTimeout(()=>{console.log(params); resolve(--params)},1000)})
})
.then((params)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{console.log(params); resolve(--params)},1000)})
})
.then((params)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{console.log(params); resolve(--params)},1000)})
})
.then((params)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{console.log(params); resolve(--params)},1000)})
})
.then((params)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{console.log(params); resolve(--params)},1000)})
})
}
fn(6)
はい、Promiseを利用すればソースコードの可読性がだいぶ向上しましたね!