SYNCERのロゴ
アイキャッチ画像

Promise - 非同期処理の完了処理

Promiseは、非同期処理が完了した後に、あらかじめ指定しておいた処理を行なうためのオブジェクトです。非同期処理が成功した時、失敗した時で処理を振り分けられます。このオブジェクトを利用することで、順序を守って処理が行なわれます。

概要

名前
Promise
仕様書
https://tc39.github.io/ecma262/#sec-promise-objects

チュートリアル

Promiseがどんな仕組みなのかを説明します。インスタンスを作成するにはコンストラクタ関数を利用します。引数(executor)には関数を指定します。この関数の指定方法は後述します。

var promise = new Promise( executor ) ;

さて、Promiseのインスタンスには3つの状態が存在します。作成した時点ではpendingです。pendingからfulfilled、またはrejectedの状態に変化したタイミングで、あらかじめ設定しておいた関数が実行されるという仕組みです。

pending
履行(fulfilled)にも、拒否(rejected)にもなっていない、保留中の状態。
fulfilled
Promiseが履行された状態。
rejected
Promiseが拒否された状態。

インスタンス作成時の引数(executor)は、2つの引数(resolveとreject)を受け取ります。どちらも関数です。第1引数(resolve)を実行すれば履行(fulfilled)に、第2引数(reject)を実行すれば拒否(rejected)に、インスタンスの状態を変更できます。

var executor = function ( resolve, reject ) {
	if ( ??? ) {
		// fulfilled
		resolve( "success!" ) ;
	} else {
		// rejected
		reject( "fail!" ) ;
	}
}

インスタンスの状態がpendingからfulfilled、またはrejectedに変わった時に実行する関数はどのように指定すればいいでしょうか。2つのインスタンスメソッドを紹介します。まずは、then()です。第1引数には履行された時(fulfilled)、第2引数には拒否された時(rejected)に実行する関数を指定できます。第2引数は省略できます。

promise.then( function () {
	// fulfilled
	console.log( "成功しました!" ) ;
}, function () {
	// rejected
	console.log( "失敗しました!" ) ;
} ) ;

もう1つはcatch()です。こちらは拒否された時(rejected)に実行する関数だけを指定できます。

promise.catch( function () {
	// rejected
	console.log( "失敗しました!" ) ;
} ) ;

具体的な例を見て下さい。下記の場合、アラートで表示されるのは1です。何故なら、setTimeout()内の処理(a=2)の前にalert()が実行されるからです。処理の完了を待たずに次の処理に進むことを非同期といいます。

var a = 1 ;

setTimeout( function () {
	a = 2 ;
}, 1000 ) ;

alert( a ) ;

Promiseを利用して、aに2が代入された後にalert()を実行したい時は、次のようにしてみましょう。

var a = 1 ;

var promise = new Promise( function ( resolve, reject ) {
	setTimeout( function () {
		a = 2 ;
		resolve() ;	// 履行の合図
	}, 1000 ) ;
} ) ;

// 履行したら行なわれる処理
promise.then( function () {
	alert( a ) ;
} ) ;

実際に想定通りに実行されるか確認してみて下さい。このようにPromiseを利用すれば、非同期処理が含まれていても各処理が実行される順序をコントロールできます。例えば、画像の読み込みが完了した時に処理を実行する、端末の位置情報を取得した時に処理を実行する、など様々です。なお、従来からある「コールバック関数やイベントなどの手法じゃダメなの?」という疑問がありそうですが、それで対応できるなら無理してPromiseを利用する必要はないと思います。ただ、Promiseを使った方がスムーズになるケースが多いので、仕組みを理解しておいて損はないはずです。

<!-- このコードは編集できます。 -->

<!DOCTYPE html>
<html>
<head>
	<style>
pre {
	white-space: pre-wrap ;
}
	</style>
</head>
<body>
<button id="run">実行</button>
<hr>
<pre id="result"></pre>
<script>
document.getElementById( "run" ).onclick = function () {
document.getElementById( "result" ).textContent = "" ;

/*** Promiseの例 ***/
var a = 1 ;

var promise = new Promise( function ( resolve, reject ) {
	setTimeout( function () {
		a = 2 ;
		resolve() ;	// 履行の合図
	}, 1000 ) ;
} ) ;

// 履行したら行なわれる処理
promise.then( function () {
	document.getElementById( "result" ).textContent = a ;
} ) ;
/*** Promiseの例 ***/

}
</script>
</body>
</html>

then()の返り値を、次のthen()で受け取ることができます。次のように連続して関数を定義できます。詳しくはthen()の説明をご参考下さい。

promise.then( /*1*/ ).then( /*2*/ ).then( /*3*/ ).catch( /*4*/ ) ;

プロパティ

prototype.constructor

コンストラクタのPromiseを返すプロパティです。

メソッド

all()

引数に指定した全てのPromiseを実行し、全ての結果を取得するメソッドです。

race()

引数に指定した全てのPromiseを実行し、最初の結果を取得するメソッドです。

reject()

引数を結果の値にして、拒否(rejected)されたPromiseを返すメソッドです。

resolve()

引数を結果の値にして、履行(fulfilled)されたPromiseを返すメソッドです。

prototype.catch()

Promiseの拒否(rejected)の結果を受けて実行される関数を指定するメソッドです。

prototype.then()

Promiseの結果を受けて実行される関数を指定するメソッドです。

サポート状況

FeaturesChromeFirefoxSafariEdgeIEOperaiOS SafariAndroid
Promise 32+ 29+ 7.1+× 19+ 8.3+×
all() 32+ 29+ 7.1+× 19+ 8.3+×
race() 32+ 29+ 7.1+× 19+ 8.3+×
reject() 32+ 29+ 7.1+× 19+ 8.3+×
resolve() 32+ 29+ 7.1+× 19+ 8.3+×
prototype 32+ 29+ 7.1+× 19+ 8.3+×
constructor 32+ 29+ 7.1+× 19+ 8.3+×
catch() 32+ 29+ 7.1+× 19+ 8.3+×
then() 32+ 29+ 7.1+× 19+ 8.3+×
  • Twitterでシェア
  • Facebookでシェア
  • Google+でシェア
  • はてなブックマークでシェア
  • pocketに保存
  • LINEでシェア
更新履歴
2017年9月30日 (土)
コンテンツを公開しました。