2016年12月16日金曜日

【JavaScript】非同期通信のためにPromiseをつかう

JavaScriptで非同期通信を利用する際に利用できるPromiseというのを勉強したのでメモ。

【参考URL】
http://qiita.com/koki_cheese/items/c559da338a3d307c9d88
https://www.htmlhifive.com/conts/web/view/study-room/async-programming-with-deferred

Promiseというのはデザインパターンのひとつらしい。
なのだけど、デザインパターンのWikipedia記事には記述がないのでデザインパターンということではないのかな。ただのライブラリ?

Promiseというのはオブジェクトである。
PHPやJSでは、関数を呼び出した際、リターンで値が戻ってこない限り次の処理に進めないので、まず素早くPromiseオブジェクトを返しておき、データの準備が整ったらそのオブジェクトを通してお目当ての値を取り出す、という仕組み。

■本来取得したい戻り値(WebAPIとの接続によって取得する値など)の代わりに「プロミスオブジェクト」という特別なオブジェクトをまず素早く返す。
■本来取得したい値を渡せる状態になったらそのプロミスオブジェクトを通して値を取り出す。
■Promiseオブジェクトのコンストラクタには、「成功時に実行する関数と失敗時に実行する関数とのふたつを引数に取る関数」を渡す。このコンストラクタにおいて、成功時のコールバックと失敗時のコールバックとの呼び出しを登録する。それぞれの実際の処理の内容をコンストラクタ内で定義するのではなく、あくまで呼び出されるタイミングを定義する。
■通信成功時の処理は呼び出し側のthen()に書く。
■通信失敗時の処理は呼び出し側のcatch()に書く。
■大抵の場合、非同期通信を使うだろうのでXMLHttpRequestインスタンスと組み合わせて使うことになると思う。Promiseコンストラクタでの「resolve(引数X)」と呼び出し側での「then(引数X)」とが対応するので、ここでXMLHttpRequestインスタンスをやり取りすると良さそう。


【サンプルコード】

index.php
<html>
<head>
<title>Promiseによる非同期処理テスト</title>
<script type="text/javascript" src="my_promise_ajax.js"></script>
<script src="https://www.promisejs.org/polyfills/promise-6.1.0.min.js"></script>
</head>

<style>
textarea{
width:600px;
height:300px;
}
</style>

<body>

<script type="text/javascript">
var promise = getContentFromUrlAsync('api01.php', '{"reward_point_bonus":{"initial":"10","limit_break":"15"}}');
promise.then(function(xmlHttp){
//非同期処理成功時の処理定義
//Promiseクラスのコンストラクタへの引数における
//「resolve」に相当する処理の内容をここで定義している。

//通信で受け取ったコンテンツ
var content = xmlHttp.responseText;

//テキストエリアにコンテンツを代入
var textarea = document.getElementById('textarea01');
textarea.value = content;
})
.catch(function(xmlHttp){
//非同期処理失敗時の処理定義
//Promiseクラスのコンストラクタへの引数における
//「reject」に相当する処理の内容をここで定義している。
alert('失敗01');
});
</script>


<h1>Promiseによる非同期処理テスト</h1>
<div>
<h2>01</h2>
<textarea id="textarea01"></textarea>
</div>
</body>
</html>

my_promise_ajax.js
////////////////////////////////////////////////////////////
//Promise関連
////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////
/**
 * Promiseインスタンスを返す関数。
 * 非同期処理を用いて同一ドメイン内APIから値を取得するために利用する。
 * この関数は汎用的に作っておき、個々の具体的な処理は呼び出し側のthen()で定義する。
 *
 * @param  string アクセスするURL(同一ドメイン内APIのURL)
 * @param  string 送信するJSON文字列
 * @return Promiseインスタンス
 */
function getContentFromUrlAsync(
api_url,
str_params_json
){
var promise = new Promise(function(resolve, reject) {
//AjaxのためのXMLHttpRequestインスタンスの作成
xmlHttp = new XMLHttpRequest();

///////////////////////
//コールバック登録。
//通信ステータスが更新されるたびに実行されるコールバック処理。
//resolve()、reject()に引数を渡してthen()、catch()で変数を使えるようにするため、
//このコールバックメソッドは外出しせずにここに無名関数として書く。
//こうすることでresolve()、reject()を引数を指定して呼び出せる。
xmlHttp.onreadystatechange = function(){
// 非同期の処理
// 処理が終わったら、resolve または rejectを呼ぶ
if(xmlHttp.readyState == 4){
//readyState : 4 通信完了

if(xmlHttp.status == 200){
//成功時
//promise.then() にXMLHttpRequestインスタンスを渡す
resolve(xmlHttp);
}else{
//失敗時
//promise.catch() にXMLHttpRequestインスタンスを渡す
reject(xmlHttp);
}//if
}//if
};
///////////////////////

//POST送信値の作成
var data = {
json : str_params_json
};

//XMLHttpRequestインスタンスでURLの値を取得
xmlHttp.open(
"POST",
api_url, //接続API(同一ドメイン内)
true
);

//サーバに対して解析方法を指定する(POST送信の場合必要)
xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

//戻り値の型を指定(JSON) 受け取り側で処理が楽になる
//→IEでは使用できないようなので不採用
//xmlHttp.responseType = 'json';

//open()で作成したリクエストを送信 POST送信値を渡す
xmlHttp.send(EncodeHTMLForm(data));
});
return promise;
}//function


//////////////////////////////
/**
 * 送信データをHTML形式にエンコードする。
 */
function EncodeHTMLForm(data){
var params = [];

for(var name in data){
var value = data[ name ];
var param = encodeURIComponent( name ) + '=' + encodeURIComponent( value );
params.push( param );
}//for

return params.join( '&' ).replace( /%20/g, '+' );
}//function


api01.php
<?php

$random_value_array = [
'太郎',
'次郎',
'三郎',
'四郎',
'五郎',
];

$json_array = [];
if(isset($_REQUEST['json'])){
$json_array = json_decode($_REQUEST['json']);
}//if

$test_array = [
'return_value' => $random_value_array[mt_rand(0, 4)],
'json'         => json_encode($json_array),
];

sleep (3);
echo(json_encode($test_array));

0 件のコメント:

コメントを投稿