プロミスは,生成時点ではまだ完了していない処理を表現するオブジェクトで,非同期処理を簡潔に記述するために役立ちます.JavaScriptの次期バージョン(ES6)で導入される予定で,ChormeやFirefoxではすでに実装されています.S-mapでは,ブラウザがプロミスを実装している場合はそれを使用し,実装されてない場合は独自実装したものを使用します.
プロミスのthen()メソッドは新たにプロミスを生成して返します.このプロミスは,最初のプロミスが成功または失敗したときに実行されるプロミスです.最初のプロミスのコールバック関数で値を返すと,then()が返すプロミスで受け取ることができます.このことを利用してプロミスのメソッドチェーンを作ることができます.
以下は,最後にコンソールにメッセージを記述する例です.
Smap.loadText( jsonsrc ).then( function( text ) { // 成功時の処理 document.getElementById( 'out1' ).innerHTML = '結果: ' + text.slice( 0, 40 ); }, function( error ) { // 失敗時の処理 document.getElementById( 'out1' ).innerHTML = error.msg; } ).then( function() { // thenメソッドはさらに新しいプロミスを返すのでメソッドチェーンでつなげられます. document.getElementById( 'out2' ).innerHTML = 'テキストファイル読み込み完了'; } );
(サンプルファイル) テキストファイルの読み込み
プロミスオブジェクトは独自に生成することも可能です.S-mapでプロミスを生成するには Smap.promise() 関数を使用します(ES6では,Promiseコンストラクタを使用します).Smap.promise()関数は引数に関数を1つだけ取り,この関数は2つの引数resolveとrejectをとり,この中で非同期処理を含む部分の処理を記述します.そして成功した場合はresolveで指定した関数を,失敗した場合はrejectで指定した関数をコールします.
Smap.loadImageの内容を記述すると以下のようになります.
promise = Smap.promise( function( resolve, reject ) { var img = new Image(); img.crossOrigin = 'anonymous'; img.onload = function() { resolve( this ); // 成功時にはresolve()をコール }; img.onerror = function() { reject( this ); // 失敗時にはreject()をコール } img.src = imgsrc; } );
(サンプルファイル) 画像ファイルの読み込み
S-mapでは,複数のプロミスの完了を待つプロミスを生成するSmap.promiseFinishing()関数が利用できます.この関数は引数にプロミスを並べるか,プロミス配列を指定すると,全てのプロミスが完了(成功または失敗)したときに実行される新しいプロミスを返します.新しいプロミスの戻り値には各プロミスの結果が配列で渡されます(失敗した場合はundefinedとなります).
// promiseFinishingの利用: すべてが完了したら成功するプロミス Smap.promiseFinishing( promise1, promise2 ).then( function( data ) { ctx.drawImage( data[0], 0, 0 ); ctx.globalAlpha = 0.7; ctx.drawImage( data[1], 0, 0 ); } );
(サンプルファイル) 画像ファイルの読み込み
Smap.promiseFinishing()は,ES6にはない独自仕様です.ES6では似た関数でall()が定義されていますが,all()は一つでも失敗するとプロミスが失敗となり,データを受け取ることもできません.
forループ内でプロミスのthen()メソッドを利用する場合は注意が必要です.then()メソッドで指定した関数は非同期で呼ばれるため,そのままではforループの終了後(カウンターが最大の時)に実行されるます.このような場合,forループ内に即時関数を作るとその中で定義した変数はそのままthen()メソッドで設定した関数内でも使用できるようになります.
通常は,以下のように渡したい変数を即時関数の引数として渡すと処理が簡潔になります.
var pList = [], url1 = 'https://gbank.gsj.jp/seamless/tilemap/basic/glfn/10/402/907.png', url2 = 'https://gbank.gsj.jp/seamless/tilemap/basic/glfn/10/402/908.png'; pList.push( Smap.loadImagePixel( url1, 10, 10 ) ); pList.push( Smap.loadImagePixel( url2, 10, 10 ) ); for( var i = 0; i < pList.length; i ++ ) { ( function( i ) { var out = document.getElementById( 'out' + ( i + 1 ) ); pList[i].then( function( data ) { out.innerHTML = 'no:' + i + ' data:' + data; } ); } )( i ); };
(サンプルファイル) ループ内のプロミスパラメータを渡す
2015年6月15日更新