はじめてのPromise、はじめての非同期関数

#はじめに
この記事では、Promiseを見たこともきいたこともない、というヒトがPromiseを乱暴に解釈しまとめています。
「ここ違うよ！」という編集リクエストは歓迎しますが、必ずしも「正しい」説明は保証できません。


###非同期関数ってそもそもなーに？
ざっくり言えば、**その場では結果が出ない関数**のことを指す。APIとかはだいたいコレだな。
たとえば、「3秒待つ」とか「ユーザーがクリックしたら起動する」とか、あるいは「どこそこと通信してそこのデータを取ってくる」とか。
ここで挙げている「今自分がいる場所の緯度経度をGPS経由で取得する」関数もその一つだけど、これなんかはGPSから位置情報が返ってくるまで「結果が出ない」だろ？そういうこった。

###Promiseとは？
コードを見やすくするために**非同期関数**に噛ませる特殊な関数のこと。
別にPromiseを使わなくても非同期関数は動くし、Promiseそれ自体は非同期関数でもなんでもないというのがポイント。
具体的には**「この処理が終わるのを待ってからあの関数を実行したい」という場合に用いる**のだ。


#Promiseを使わずに関数を順番に実行する例
基本的に、関数の中で関数を呼び出す、というやりかたとなる。

###非同期関数を使わない場合

```javascript:javascript

function Ichi_Ban_kansu(){


	console.log("おどれら、");
	Ni_Ban_kansu();
}
function Ni_Ban_kansu(){
	console.log("ウチのシマで何さらしとんじゃ……");
}

Ichi_Ban_kansu();

```


###非同期関数（今回は例として`setTimeout()`）を使う場合

```javascript:javascript

function Ichi_Ban_kansu(){
	console.log("おどれら、");
	setTimeout(function(){

		Ni_Ban_kansu();
	}, 1000);
}
function Ni_Ban_kansu(){
	console.log("ウチのシマで何さらしとんじゃ……");
}

Ichi_Ban_kansu();

```

ふつうにやると、関数の中に関数を書くことになる。
これを繰り返せば当然、コードは段々畑のように入り組んでいき、読みづらくなる。
んで、その段々畑にしたままで読みづらくなってる状態のコードはコールバック地獄(?)と呼ばれ、弾圧される運命にある。


今回`setTimeout`という非同期関数を使っている……のだが、ここで一つ例をお見せしよう。

```javascript:javascript

function Ichi_Ban_kansu(value){
	setTimeout(function(){
		var name = '暗黒' + value;
		return name;
	}, 1000);
}

var ryouri = Ichi_Ban_kansu('おやき');
console.log(ryouri);

```

たとえば、setTimeoutを使いたいけれど、この部分だけ関数として分割したい＆中で使った値を関数の外へと出力したい場合。
パッと思いつくのは上のように値をreturnで返すやり方だが……コレ、不正解である。こういうことをしても、「暗黒おやき」という文字列は出てこない。
理由は簡単、**非同期関数の内側では、returnで値を返すことができないのである。**


#実践例：Promiseと非同期関数を書いてみる

```javascript:javascript

// 関数の宣言と処理
function watchCurrent(){
	return new Promise(function(resolve, reject){
		/* この中に非同期関をかく！ */

		var watchID;
		var options = {
			'enableHighAccuracy': true, // 位置情報の精度を高く
			'timeout': 20000, // 20秒でタイムアウト
			'maximumAge': 300000 // 5分間有効
		};
		watchID = navigator.geolocation.watchPosition(
			function(position){
			// 「非同期関数が」成功したときの処理。

				var result = {};
				// 緯度
				result.latitude = position.coords.latitude;
				// 経度
				result.longitude = position.coords.longitude;


				// 値を返す（関数の外に出力する）ときには、returnではなくこのresolveを使う。
				// 今回のように、外に出したい値が２つ以上ある場合は配列にしたりオブジェクトにしてまとめよう。
				resolve(result);
			},
			function(error){
			// 「非同期関数が」失敗したときの処理。
			// 通信に失敗した、ユーザーに位置情報の提供を拒否された、
			// 位置情報がいつまでたっても送信されてこないなどのイレギュラーが発生したとき、
			// この２番目の関数が実行されるのだ。Promiseを使うときは、ここにrejectを噛ませてやろう。

				// 値を返す（関数の外に出力する）とき、returnではなくrejectを使う。
				// 「値を返す」という動作はresolveと同じ。
				reject(error.message);
			}, options);

		/* 非同期関数ここまで！ */
	});
}

// 関数の呼び出し＆実行
watchCurrent().then(function(seikou){
	// 「Promiseが」成功したときの処理

	console.log(seikou);
	alert('緯度： ' + seikou.latitude + '\n経度： ' + seikou.latitude);
	
}).catch(function(sippai){
	// 「Promiseが」失敗したときの処理

	console.log(sippai);
	alert('どうやら、僕たちは失敗したらしい。');
});

```

非同期関数を使いつつ、中で扱った値を外に出したい場合。
そんな時はreturnの代わりに**resolve**と**reject**を使ってやればよい。
（なお、resolve＆rejectはPromiseと一体化した関数なので、Promiseをつけずにresolveだけ使うなんてマネもできない。）

そもそもPromiseを使う最大の意義はナニかというと、**コードを見やすくすること**だ。
だから、やり方としてはこのように、非同期通信がからむ部分だけをひとつの関数にまとめて書くのがベターである。

注意すべき点として、**非同期関数**(今回はwatchPosition)**とPromiseそれぞれに成功時、失敗時の処理がある**ということが挙げられる。（ないヤツもあるよ。setTimeoutとかaddEventListenerとか。）
watchPositionは上でも書いたとおり、通信に成功したときに`function(position){ ~`、通信に失敗したときに`function(error){ ~`の処理が実行される(ちなみに、失敗時は引数であるerrorにエラーメッセージが代入される。)。

じゃあPromiseはどうなのかというと、**値がresolveで返されたか、rejectで返されたか**で成否判定をする。
Promiseは成功時に`then`処理を、失敗時に`catch`処理を行うのだが、
早い話、resolveとrejectの位置を入れ替えて以下のようにしてしまえば、通信に成功したときに`catch`、失敗したときに`then`の処理を実行する、なんてことができる。

```javascript:javascript

// 関数の宣言と処理
function watchCurrent(){
	return new Promise(function(resolve, reject){
		/* この中に非同期関をかく！ */

		var watchID;
		var options = {
			'enableHighAccuracy': true, // 位置情報の精度を高く
			'timeout': 20000, // 20秒でタイムアウト
			'maximumAge': 300000 // 5分間有効
		};
		watchID = navigator.geolocation.watchPosition(
			function(position){
			// 「非同期関数が」成功したときの処理。

				var result = {};
				// 緯度
				result.latitude = position.coords.latitude;
				// 経度
				result.longitude = position.coords.longitude;

				reject(result);
			},
			function(error){
			// 「非同期関数が」失敗したときの処理。
			// 通信に失敗した、ユーザーに位置情報の提供を拒否された、
			// 位置情報がいつまでたっても送信されてこないなどのイレギュラーが発生したとき、
			// この２番目の関数が実行されるのだ。Promiseを使うときは、ここにrejectを噛ませてやろう。

				// error.messageの部分にはエラー時のメッセージが入っているが、resolveを使うとこのエラーメッセージは成功扱いとして吐き出される。
				resolve(error.message);
			}, options);

		/* 非同期関数ここまで！ */
	});
}

// 関数の呼び出し＆実行
watchCurrent().then(function(seikou){
	// 「Promiseが」成功したときの処理

	console.log(seikou);
	alert('緯度： ' + seikou.latitude + '\n経度： ' + seikou.latitude);
	
}).catch(function(sippai){
	// 「Promiseが」失敗したときの処理

	console.log(sippai);
	alert('どうやら、僕たちはまた失敗したらしい。');
});

```


ちなみに、メジャーな記事ではちょっと捻って以下のような書き方をしていることが多い。
なお、内容は一番上のやつとまったく同じなので、コピペして見比べてみるとよいかも。

```javascript:javascript

// 関数の宣言と処理
var watchCurrent = function(){
	return new Promise(resolve, reject) => {
		/* この中に非同期関をかく！ */

		var watchID;
		var options = {
			'enableHighAccuracy': true, // 位置情報の精度を高く
			'timeout': 20000, // 20秒でタイムアウト
			'maximumAge': 300000 // 5分間有効
		};
		watchID = navigator.geolocation.watchPosition(
			function(position){
			// 「非同期関数が」成功したときの処理。

				var result = {};
				// 緯度
				result.latitude = position.coords.latitude;
				// 経度
				result.longitude = position.coords.longitude;


				// 値を返す（関数の外に出力する）ときには、returnではなくこのresolveを使う。
				// 今回のように、外に出したい値が２つ以上ある場合は配列にしたりオブジェクトにしてまとめよう。
				resolve(result);
			},
			function(error){
			// 「非同期関数が」失敗したときの処理。
			// 通信に失敗した、ユーザーに位置情報の提供を拒否された、
			// 位置情報がいつまでたっても送信されてこないなどのイレギュラーが発生したとき、
			// この２番目の関数が実行されるのだ。Promiseを使うときは、ここにrejectを噛ませてやろう。

				// 値を返す（関数の外に出力する）とき、returnではなくrejectを使う。
				// 「値を返す」という動作はresolveと同じ。
				reject(error.message);
			}, options);

		/* 非同期関数ここまで！ */
	};
}

// 関数の呼び出し＆実行
watchCurrent.then(function(seikou){
	// 「Promiseが」成功したときの処理

	console.log(seikou);
	alert('緯度： ' + seikou.latitude + '\n経度： ' + seikou.latitude);
	
}).catch(function(sippai){
	// 「Promiseが」失敗したときの処理

	console.log(sippai);
	alert('いい加減飽きてはくれないだろうか。');
});
```

##複数のPromiseをリレーさせる(名前あり関数)
たとえば、関数Aで加工して出した値を関数Bでまた加工したい、という場合にはこのやり方が有効だ。

```javascript:javascript

function TypeA(kazu){
    return new Promise(function(resolve, reject){

        // kazuに4を足す。kazuが数字でないならエラーを出す。
        if( typeof kazu == 'number' ){
            var result = Number(kazu) + 4;
            resolve(result);
        }else{
            reject('これ、数字じゃないです。');
        }

    });
}

function TypeB(math){
    return new Promise(function(resolve, reject){

        // TypeAから渡されたmathの中身が7より大きい数字なら計算を続け、そうでないならエラーとしてrejectを出す。
        if(math > 7){
            console.log(math);
            var result = Number(math) + 8;
            resolve(result);
        }else{
            reject('mathが7以下なので計算をやめてしまいました。あーあ。');
        }

    });
}

var targetnum = 4;
TypeA(targetnum).then(TypeB).then(function(result){
    // TypeAとTypeBの両方でエラーが出なかったらここの処理が行われる
    console.log(result);

}).catch(function(message){
    // 失敗時の処理。TypeAかTypeB、どちらかが失敗したらその時点でここの処理が行われる
    console.log(message);

});

```

出力内容は以下のようになる。
![1577083893919.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416995/21a8b450-d457-012d-6a43-9022f0cda99e.jpeg)

ちなみに、ここでポイント。
名前あり関数を連結するとき、TypeBの引数[^e2]が指定されていないのにお気づきだろうか。
引数はどうするのか、というと、実はコレ、**指定できない。**
もうすこし正確に言えば、TypeAでresolveした値がそのまま自動的に、TypeBの引数になる……引き継がれるのである。

というわけなので、TypeA、TypeBでresolveできる値は一つだけ。
仮に２つ以上ある場合、resolveの中身は最初の１つだけに格納される。２つ以上の値が必要？配列でなんとかしよう。

当たり前だが、返り値はきちんと返さないと引き継がれない。
面倒くさがってresolveを書きもらすと、TypeAで使ったresultを利用できないから注意だ。

[^e2]: 本来ならTypeA(targetnum)のように「TypeB( 引数 )」という書き方をするはずである。

##複数のPromiseをリレーさせる(名前なし関数)

```javascript:javascript

function TypeA(kazu){
    return new Promise(function(resolve, reject){

        // kazuに4を足す。kazuが数字でないならエラーを出す。
        if( typeof kazu == 'number' ){
            var result = Number(kazu) + 4;
            resolve(result);
        }else{
            reject('これ、数字じゃないです。');
        }

    });
}

var targetnum = 4;
TypeA(targetnum).then(function(math){

    // TypeAから渡されたmathの中身が7より大きい数字なら計算を続け、そうでないならエラーとしてrejectを出す
    if(math > 7){
        console.log(math);
        var result = Number(math) + 8;
        // then(function(){ ~~~ })の中なら、resolveではなくreturnで値を返してＯＫ。逆にPromiseがないのでresolveはここでは使えない
        return result;
    }else{
        // thenの中でエラーを出力するときはrejectでなくthrowを使う。
        // throw '足しても8にならなかったよ…';  <= この書き方でもＯＫだが、 new Errorと一緒に使うのがベターらしい。
        throw new Error('mathが7以下なので計算をやめてしまいました。あーあ。');
    }

}).then(function(result){

    // TypeAとそのあとに続くthenの両方でエラーが出なかったらここの処理が行われる
    console.log(result);

}).catch(function(message){

    // 失敗時の処理。TypeAかそのすぐ後のthen、どちらかが失敗したらその時点でここの処理が行われる
    console.log(message);

});
```

上の方で「resolveとrejectはPromiseと一緒でなければ使えない」と書いたが、これがその代表格みたいなもん。
ここでは、resolveの代わりにreturn、rejectの代わりにthrowを使っている。



あ、そうそう。
`setTimeout()`やアニメーション処理など、確実に成功することが分かっている処理に関しては、**失敗時の処理を書かない**というのもアリだろう。


```javascript:javascript
function Three_counts(){
	return new Promise(function(resolve){

		setTimeout(function(){

			resolve('3秒経ったよ！');
		}, 3000);
	});
}


Three_counts().then(function(text){

	console.log(text);
	return text;
}).then(function(text){

	console.log(text + 'までに、3秒かかりました。');
});
```

![1582213678823.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416995/b98e1e21-2f77-0df3-3659-2693aac25bfe.jpeg)



#Promise.all
２つ以上の関数を同時に処理し、両方の値が揃ってから計算をしたい場合は「Promise.all」という関数も使える。

```javascript:javascript
function TypeA(kazu){
	return new Promise(function(resolve, reject){

		// 2秒経過したら4を足す。kazuが数字でなければエラーを返す。
		setTimeout(function(){
			if(typeof kazu == 'number'){
				var result = kazu + 4;
				resolve(result);
			}else{
				reject(kazu + '、数字じゃないです。');
			}
		}, 2000);

	});
}


function TypeB(math){
	return new Promise(function(resolve, reject){

		// 5秒経過したら8を足す。mathが数字でなければエラーを返す。
		setTimeout(function(){
			if(typeof math == 'number'){
				var result = math + 4;
				resolve(result);
			}else{
				reject(math + '、数字じゃないです。');
			}
		}, 5000);

	});	
}


Promise.all([TypeA(4), TypeB(8)]).then(function(answer){

	// answerは配列として返される。
	console.log(answer);

	// それぞれ別処理したいなら、このように書こう。
	console.log(answer[0]);
	console.log(answer[1]);

}).catch(function(error){

	// どちらかがエラーになった時点でこのcatch処理に進むことになる。
	// この性質上、errorに入るのは先に出たrejectだけ。
	// たとえば両方エラーだった場合、先に処理が終わるTypeAのエラーメッセージがここに入るというわけだ。
	console.log(error);
});
```

出力するとこうなる。
![1584865991067.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416995/768652cf-49e3-5f71-12d5-4ecf280ca405.jpeg)


#ちょっとしたおまけ：「async await」
Promiseよりさらに進化して使いやすくなったと噂のコード……**async/await**の比較も載せておこう。
以下の２種のコードはどちらも同じ効果を持つ。


###Promiseのみ
```javascript:javascript
function ThreeSecond(){
	return new Promise(function(resolve){
		setTimeout(function(){
			resolve('3秒経過しました');
		}, 3000);
	});
}

function Result(){
	ThreeSecond().then(function(answer){
		console.log(answer);
	});
}

Result();
```

###async awaitも使ったバージョン
```javascript:javascript
function ThreeSecond(){
	return new Promise(function(resolve){
		setTimeout(function(){
			resolve('3秒経過しました');
		}, 3000);
	});
}

async function Result(){
	var answer = await ThreeSecond();
	console.log(answer);
}

Result();
```

###async awaitをさらに応用
```javascript:javascript


async function Result(){
	function funcSuc(position){

		var answer = {};
		// 緯度
		answer.latitude = position.coords.latitude;
		// 経度
		answer.longitude = position.coords.longitude;

		console.log(answer);
	}

	// 冒頭に出てきたGPSの関数。こういう書き方をすると、このawaitがある行の処理が終わるまでconsole.log("end.")は実行されない。
	// 成功時はfuncSucが実行される。
	await navigator.geolocation.watchPosition(funcSuc);
	console.log("end.");
}

Result();

```

async, awaitは、`function ThreeSecond`にあたる部分はpromiseを利用したFunctionに対して使う必要はない。
REST API など、外部サービスが提供している非同期関数に対して利用することも可能だ。つまり、「すぐには答えが出ない処理」であるなら、このasync/awaitの出番というわけだ。



#参考資料

https://noumenon-th.net/programming/2018/12/14/promise1/
https://rightcode.co.jp/blog/information-technology/javascript-async-await
https://www.wareko.jp/blog/concatenate-javascript-promise-in-multiple-stages
https://qiita.com/matsuby/items/3f635943f25e520b7c20
https://qiita.com/YoshikiNakamura/items/732ded26c85a7f771a27
https://qiita.com/kiyodori/items/da434d169755cbb20447
https://qiita.com/don-bu-rakko/items/b283829c4572a6425a5c
https://qiita.com/nekoneko-wanwan/items/f6979f687246ba089a35
https://memo.appri.me/programming/gsi-geocoding-api#%E9%80%86%E3%82%B8%E3%82%AA%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0API
https://qiita.com/YoshikiNakamura/items/732ded26c85a7f771a27#%E9%9D%9E%E5%90%8C%E6%9C%9F%E5%87%A6%E7%90%86%E3%82%92%E8%A1%8C%E3%81%AA%E3%81%86%E9%96%A2%E6%95%B0%E3%82%92%E4%BD%9C%E3%82%8Bpromise%E7%89%88
http://www.tohoho-web.com/ex/promise.html
https://qiita.com/toshihirock/items/e49b66f8685a8510bd76
https://www.sejuku.net/blog/52314
https://noumenon-th.net/programming/2018/12/14/promise1/
https://noumenon-th.net/programming/2018/12/14/async1/
https://webbibouroku.com/Blog/Article/promises