ホームページ > ウェブフロントエンド > jsチュートリアル > jQuery Deferred と Promise_jquery を使用した応答性の高いアプリケーションの作成に関する詳細な紹介

jQuery Deferred と Promise_jquery を使用した応答性の高いアプリケーションの作成に関する詳細な紹介

WBOY
リリース: 2016-05-16 17:40:50
オリジナル
828 人が閲覧しました

この記事では、JavaScript における Deferred と Promise の概念について説明します。これらは JavaScript ツールキット (Dojo や MochiKit など) の非常に重要な機能であり、最近、人気のある JavaScript ライブラリ jQuery (すでにバージョン 1.5 で導入されています) でデビューしました。 。 Deferred は、将来のある時点で応答を返すことを目的とした「promise」オブジェクトを作成することにより、抽象的な非ブロッキング ソリューション (Ajax リクエストへの応答など) を提供します。これまで「約束」に出会ったことがない場合は、以下で詳しく説明します。

抽象的に言えば、遅延は、ブロック関数と比較して、アプリケーションが完了するのをブロックするのではなく、完了までに時間がかかる操作を表す方法として理解できます。そして結果を返します。遅延オブジェクトはすぐに戻ります。その後、コールバック関数を遅延オブジェクトにバインドできます。コールバック関数は、非同期処理の完了後に呼び出されます。

Promise
Promise と deferred の実装の詳細に関する情報を読んだことがあるかもしれません。この章では、Promise がどのように機能するかを簡単に紹介します。これは、遅延をサポートするほとんどすべての JavaScript フレームワークに適用できます。

一般に、Promise は、ソフトウェア エンジニアリングにおける遅延 (または将来) の概念を説明するための解決策を提供するモデルとして機能します。その背後にある考え方はすでに紹介されています。メソッドを実行して結果が返されるのを待つアプリケーションをブロックする代わりに、将来の値を満たすために Promise オブジェクトが返されます。

サードパーティ API からのデータに大きく依存する Web アプリケーションを構築すると、理解に役立ちます。その場合、よくある問題に直面します。API 応答の遅延時間を知ることができず、結果が返されるまでアプリケーションの他の部分がブロックされる可能性があります。遅延は、ノンブロッキングでコードから完全に切り離されているため、この問題に対するより良い解決策を提供します。

Promise/プロポーザルは、ハンドラー関数が結果を返すときに実行されるコールバックを登録するための「then」メソッドを定義します。 Promise を返すための疑似コードは次のようになります:

コードをコピー コードは次のとおりです:

promise = callToAPI( arg1, arg2, ...);
promise.then(function( futureValue ) {
/* ハンドル futureValue */
}); (function( futureValue ) {
/* do something else */
});


さらに、Promise コールバックは次の 2 つの異なる状態で実行されます。

•resolved: この場合、データは利用可能です
•rejected: この場合、エラーが発生し、利用可能な値はありません
幸いなことに、「then」メソッドは 2 つのパラメータを受け入れます。 1 つは約束が解決されたとき (resolved) で、もう 1 つは約束が拒否されたとき (rejected) です。疑似コードに戻りましょう:


コードをコピーします コードは次のとおりです:
約束。 then( function( futureValue ) {
/* 値を取得しました */
} , function() {
/* 何か問題がありました */
} ); >


場合によっては、アプリケーションを続行する前に複数の結果を取得する必要があります (たとえば、ユーザーが関心のあるオプションを選択する前に、動的なオプションのセットを表示するなど)。この場合、「when」メソッドを使用して、すべての約束が満たされた後にのみ実行を続行できるというシナリオを解決できます。



when(
promise1 ,
promise2,
...
).then(function( futureValue1, futureValue2, ... ) {
/* すべての Promise が完了し、解決されています */
});



その良い例は、複数のアニメーションを同時に実行するシナリオです。 各アニメーション実行後のコールバックを追跡しないと、アニメーションの完了後に次のタスクを実行することが困難になります。ただし、Promise と 'when' メソッドを使用すると、非常に簡単になります。アニメーションが完了したら、次のタスクを実行できます。最終的な結果は、コールバックを使用するだけで、複数のアニメーションの実行結果を待機する問題を解決できることです。 例:



when( function (){
/* アニメーション 1 */
/* プロミス 1 を返す */
}, function(){
/* アニメーション 2 */
/* プロミス 2 を返す */
} ).then(function(){
/* 両方のアニメーションが完了したら、追加のロジックを実行できます */
});
这意味着,基本上可以用非阻塞的逻辑方式编写代码并异步执行。 而不是直接将回调传递给函数,这可能会导致紧耦合的接口,通过promise模式可以很容易区分同步和异步的概念。

  在下一节中,我们将着眼于jQuery实现的deferreds,你可能会发现它明显比现在所看到的promise模式要简单。

jQuery的Deferreds
  jQuery在1.5版本中首次引入了deferreds。它 所实现的方法与我们之前描述的抽象的概念没有大的差别。原则上,你获得了在未来某个时候得到‘延时'返回值的能力。在此之前是无法单独使用的。 Deferreds 作为对ajax模块较大重写的一部分添加进来,它遵循了CommonJS的promise/ A设计。1.5和先前的版本包含deferred功能,可以使$.ajax() 接收调用完成及请求出错的回调,但却存在严重的耦合。开发人员通常会使用其他库或工具包来处理延迟任务。新版本的jQuery提供了一些增强的方式来管理 回调,提供更加灵活的方式建立回调,而不用关心原始的回调是否已经触发。 同时值得注意的是,jQuery的递延对象支持多个回调绑定多个任务,任务本身可以既可以是同步也可以是异步的。

  您可以浏览下表中的递延功能,有助于了解哪些功能是你需要的:
jQuery.Deferred() 创建一个新的Deferred对象的构造函数,可以带一个可选的函数参数,它会在构造完成后被调用。
jQuery.when() 通过该方式来执行基于一个或多个表示异步任务的对象上的回调函数
jQuery.ajax() 执行异步Ajax请求,返回实现了promise接口的jqXHR对象
deferred.then(resolveCallback,rejectCallback) 添加处理程序被调用时,递延对象得到解决或者拒绝的回调。
deferred.done()

当延迟成功时调用一个函数或者数组函数.

deferred.fail()

当延迟失败时调用一个函数或者数组函数.。

deferred.resolve(ARG1,ARG2,...) 调用Deferred对象注册的‘done'回调函数并传递参数
deferred.resolveWith(context,args) 调用Deferred对象注册的‘done'回调函数并传递参数和设置回调上下文
deferred.isResolved 确定一个Deferred对象是否已经解决。
deferred.reject(arg1,arg2,...) 调用Deferred对象注册的‘fail'回调函数并传递参数
deferred.rejectWith(context,args) 调用Deferred对象注册的‘fail'回调函数并传递参数和设置回调上下文
deferred.promise() 返回promise对象,这是一个伪造的deferred对象:它基于deferred并且不能改变状态所以可以被安全的传递

jQuery 遅延実装の中核は、チェーン内で呼び出すことができるコンストラクターである jQuery.Deferred です。 ...遅延オブジェクトのデフォルト状態は未解決であり、コールバックは .then() または .fail() メソッドを通じてキューに追加され、プロセスの後半で実行されることに注意してください。

複数のパラメーターを受け入れる $.when() の次の例
コードをコピー コードは次のとおりです。

関数 successFunc(){ console.log( "成功!" ) }
関数 FailureFunc(){ console.log( "失敗!" ); $.when(
$.ajax( "/main.php" ),
$.ajax( "/modules.php" ),
$.ajax( "/lists.php" )
) .then( successFunc, FailureFunc );



$.when() の実装で興味深いのは、遅延オブジェクトを解析できるだけでなく、そうでないパラメータも渡すことができることです。遅延オブジェクトは処理時に遅延オブジェクトとして扱われ、コールバック (doneCallbacks) がすぐに実行されます。 これは、jQuery の Deferred 実装でも言及する価値があります。さらに、 deferred.then() は、遅延のキューにコールバックを追加するための deferred.done メソッドと deferred.fail() メソッドのサポートも提供します。

前に紹介した表に記載されている deferred 関数を使用して、コード例を見てみましょう。 ここでは、非常に基本的なアプリケーションを作成します。(1) $.get メソッド (Promise を返す) を介して外部ニュース ソースを取得し、(2) 最新の応答を取得します。 同時に、プログラムは関数 (prepareInterface()) を通じてニュースと返信コンテンツ表示コンテナーのアニメーションも実装します。

他の関連アクションを実行する前に上記の 3 つの手順が確実に完了するように、$.when() を使用します。 .then() ハンドラーと .fail() ハンドラーを使用して、ニーズに応じて他のプログラム ロジックを実行できます。



function getlatestNews() {
return $.get( “latestNews.php”, function(data){
console.log( “受信したニュースデータ” );
$( “.news” ).html(data);
} ) ;
}
function getlatestReactions() {
return $.get( “latestReactions.php”, function(data){
console.log( “受信したリアクションデータ” );
$ ( ".reactions" ).html(data);
} );

function prepareInterface() {
return $.Deferred(function( dfd ) {
varlatest = $( ".news, .reactions" );
latest.slideDown( 500, dfd.resolve );
latest.addClass( "active" ).promise( );
}

$.when(
getlatestNews(), getlatestReactions(), prepareInterface()
).then(function(){
console.log( “fireリクエストが成功した後 ” );
}).fail(function(){
console.log( “何か問題が発生しました!” );
});


が遅延しましたajax のシーン 運用で使用しても、他の場所では使用できないというわけではありません。 このセクションでは、遅延を使用することで非同期動作を抽象化し、コードを分離するのに役立ついくつかのソリューションを見ていきます。

非同期キャッシュ

非同期タスクに関しては、タスクが同じキーに対して 1 回だけ実行されるようにする必要があるため、キャッシュは少し要求が厳しい場合があります。したがって、コードは何らかの方法で受信タスクを追跡する必要があります。 たとえば、次のコード スニペット:



コード
をコピーします。 コードは次のとおりです: $.cachedGetScript ( url, callback1 ); $.cachedGetScript( url, callback2 );


キャッシュ メカニズムは、スクリプトが 1 回のみリクエストできるようにする必要があります。キャッシュにすでに存在するかどうかは関係ありません。 したがって、キャッシュ システムがリクエストを正しく処理するには、最終的には、特定の URL にバインドされたコールバックを追跡するロジックを記述する必要があります。

ありがたいことに、これはまさにロジック遅延実装の一種なので、次のように実行できます:





コードをコピー
コードは次のとおりです: var queuedScriptPromises = {}; $.cachedGetScript = function( url, callback ) {
if ( !cachedScriptPromises[ url ] ) {
cachedScriptPromises[ url ] = $.Deferred(function( defer ) {
$.getScript( url ).then( defer.resolve, defer.reject );
}).promise();
}
return queuedScriptPromises[ url ].done( callback )


;
コードは非常に単純です。URL ごとに Promise オブジェクトをキャッシュします。 指定された URL に Promise がない場合は、遅延を作成してリクエストを行います。 すでに存在する場合は、コールバックをバインドするだけです。 このソリューションの大きな利点は、新しいリクエストとキャッシュされたリクエストを透過的に処理できることです。 もう 1 つの利点は、遅延ベースのキャッシュが障害状況を適切に処理することです。 Promise が「拒否」ステータスで終了した場合、テストするためのエラー コールバックを提供できます:

$.cachedGetScript( url ).then( successCallback, errorCallback );

覚えておいてください: いいえリクエストがキャッシュされているかどうかに関係なく、上記のコード スニペットは正常に動作します。

ユニバーサル非同期キャッシュ
コードを可能な限り一般化するために、キャッシュ ファクトリを構築し、実際に実行する必要があるタスクを抽象化します。

コードをコピー コードは次のとおりです。

$.createCache = function( requestFunction ) {
var キャッシュ = {} ;
return function( key, callback ) {
if ( !cache[ key ] ) {
cache[ key ] = $.Deferred(function( defer ) {
requestFunction( 遅延、キー ) ;
}).promise();
キャッシュを返す
}



特定のリクエスト ロジックが抽象化されたので、cachedGetScript を書き直すことができます。



$.cachedGetScript = $.createCache(function( defer, url ) {
$.getScript( url ).then( defer.resolve, defer.reject );
});



createCache を呼び出すたびに、新しいキャッシュ ライブラリが作成され、新しいキャッシュ取得関数が返されます。これで、キャッシュから値を取得する論理シナリオを簡単に実装できる汎用キャッシュ ファクトリが完成しました。


画像の読み込み
もう 1 つの候補シナリオは画像の読み込みです。同じ画像を 2 回読み込まないようにしてください。画像の読み込みが必要になる場合があります。 createCache を使用すると簡単に実装できます:

コードをコピーします コードは次のとおりです:
$ .loadImage = $.createCache(function( defer, url ) {
var image = new Image();
function cleanUp() {
image.onload = image.onerror = null;
}
defer.then( cleanUp, cleanUp );
image.onload = function() {
defer.resolve( url );
image.onerror = defer.reject; 🎜>image.src = url;
});



次のコード スニペットは次のとおりです:




コードをコピーします
コードは次のとおりです。 $.loadImage( "my-image.png" ).done( callback1 ); .loadImage( "my-image.png" " ).done( callback2 );

image.png がロードされているか、ロード中であるかに関係なく、キャッシュは正常に機能します。ロードされています。


API レスポンスのキャッシュ データ

ページのライフサイクルを通じて不変とみなされている API リクエストも、キャッシュの最適な候補です。 たとえば、次の操作を実行します。


コードをコピーします
コードは次のとおりです: $.searchTwitter = $.createCache(function( defer, query ) { $.ajax({ url: "http://search.twitter.com/search.json",
data: { q: クエリ },
dataType: "jsonp",
成功: defer.resolve,
エラー: defer.reject
});



プログラム キャッシュしながら Twitter で検索を実行できます:



コードをコピーします

コードは次のとおりです。 $.searchTwitter( "jQuery 遅延", callback1 ); $.searchTwitter( "jQuery 遅延", callback2 );
タイミング
遅延ベースのキャッシュはネットワーク リクエストに限定されず、タイミング目的にも使用できます。

たとえば、気づきにくい特定の機能にユーザーの注意を引いたり、遅延の問題に対処したりするために、Web ページ上で一定の時間が経過した後にアクションを実行する必要がある場合があります。 setTimeout はほとんどのユースケースに適していますが、タイマーが起動した後、または理論的には期限切れになった後は解決策がありません。 次のキャッシュ システムを使用してこれを処理できます:
コードをコピー コードは次のとおりです:

varreadyTime ;
$(function() {readyTime = jQuery.now(); });
$.afterDOMReady = $.createCache(function( defer, late ) {
lay = 遅延 | | 0;
$(function() {
var delta = $.now() - readyTime;
if ( delta >= 遅延 ) { defer.resolve(); }
else {
setTimeout ( defer.resolve, late - delta );
}
});


新しい afterDOMReady ヘルパー メソッドは domReady に最小限の機能を提供します適切な時間後にカウンターします。 遅延時間が経過すると、コールバックはすぐに実行されます。


複数のアニメーションを同期する アニメーションは、非同期タスクのもう 1 つの一般的な例です。 ただし、無関係なアニメーションがいくつか完了した後にコードを実行するのは、まだ少し困難です。アニメーション要素の Promise オブジェクトを取得する機能は jQuery 1.6 でのみ提供されていますが、手動で簡単に実装できます:

コードをコピーコードは次のとおりです。
$.fn.animatePromise = function( prop, Speed, easing, callback) {
var elements = this;
return $.Deferred(function ( defer ) {
elements.animate( prop, Speed, easing, function() {
defer.resolve();
if ( callback ) {
callback.apply( this, argument );
}
});
});
};


その後、$.when() を使用して、異なるアニメーション:


コードをコピー コードは次のとおりです:
var fadeDiv1Out = $ ( " #div1" ).animatePromise({ opacity: 0 }),
fadeDiv2In = $( "#div1" ).animatePromise({ opacity: 1 }, "fast" ); when( fadeDiv1Out, fadeDiv2In ).done(function() {
/* 両方のアニメーションが終了しました */
});



同じトリックを使用して、補助メソッド:



コードをコピー コードは次のとおりです: $。 each([ " slideDown", "slideUp", "slideToggle", "fadeIn", "fadeOut", "fadeToggle" ],
function( _, name ) {
$.fn[ name "Promise" ] = function( 速度 , イージング, コールバック ) {
var 要素 = this;
return $.Deferred(function( defer ) {
要素[名前]( 速度, イージング, function() {
defer.resolve( );
if (コールバック) {
callback.apply(
}
}); ;
});



次に、次のように新しいヘルパー コードを使用してアニメーションを同期します。
コードをコピー

コードは次のとおりです:


$.when(
$( "#div1" ).fadeOutPromise(), $( "#div2" ).fadeInPromise ( "fast" ) ).done(function() { /* 両方のアニメーションが完了しました */ }); 🎜>1 回限りのイベント
jQuery は必要となる可能性のあるすべてのバインディング メソッドを提供しますが、イベントを 1 回だけ処理する必要がある場合は、少し難しくなる可能性があります。 ( $.one() とは異なります)

たとえば、最初にクリックされたときにパネルを開き、パネルが開いた後に特定の初期化ロジックを実行するボタンが必要な場合があります。 この状況に対処するとき、人々は通常次のようなコードを書きます:





コードをコピー

コードは次のとおりです:

var buttonClicked = false;
$( "#myButton" ).click(function() {
if ( !buttonClicked ) {
buttonClicked = true;
initializeData( ); showPanel(); } });
パネルが開いた後に、ボタンがクリックされたときのアクションを追加できるようになります。





コードをコピー


コードは次のとおりです:

if ( buttonClicked ) { /* 特定のアクションを実行 */ }

これは非常に結合されたソリューションです。 他の操作を追加する場合は、バインド コードを編集するか、コピーを作成する必要があります。 そうでない場合、唯一のオプションは buttonClicked をテストすることです。 buttonClicked は false である可能性があるため、新しいコードは決して実行されず、その結果、この新しいアクションが失われる可能性があります。

遅延を使用すると、より良い結果が得られます (簡単にするために、以下のコードは 1 つの要素と 1 つのイベント タイプにのみ適用されますが、複数のイベント タイプのコレクションに簡単に拡張できます):

コードをコピーします コードは次のとおりです:

$.fn.bindOnce = function(event, callback ) {
var element = $( this[ 0 ] ),
defer = element.data( "bind_once_defer_" イベント );
if ( !defer ) {
defer = $.Deferred() ;
関数 deferCallback() {
element.unbind(event, deferCallback );
defer.resolveWith(this, argument );
element.bind(event , deferCallback ); >element.data( "bind_once_defer_" イベント , defer );
}
return defer.done( callback ).promise();


コードは次のように動作します。
• 要素に指定されたイベントにバインドされた遅延オブジェクトがあるかどうかを確認します
• ない場合は、最初にイベントを起動するようにオブジェクトを作成します。
を解決します。 • 次に、指定されたオブジェクトをバインドします。遅延に対するコールバックと Promise を返します
コードは冗長ですが、関連する問題の処理が簡素化されます。 まずヘルパー メソッドを定義しましょう:




コードをコピーします コードは次のとおりです: $ .fn.firstClick = function( callback ) {
return this.bindOnce( "click",
};



次に、ロジックは次のように再構築できます:



コードをコピー コードは次のとおりです: var openPanel = $( "# myButton" ).firstClick();
openPanel.done(initializeData );



パネルが開いた後、必要なのはこれだけです:



コードをコピーします
コードは次のとおりです。 openPanel .done(function() { /* 特定のアクションを実行します */ });

パネルが開いていない場合、アクションはボタンがクリックされるまで遅延されます。

Composition Assistant

上記の例を個別に見てみると、promise の役割は限られています。 ただし、約束の本当の力は、それらを組み合わせることにあります。
パネルのコンテンツをロードし、最初のクリックでパネルを開きます


パネルを開き、そのコンテンツをリクエストしてからコンテンツをフェードインするボタンがあるとします。前に定義したヘルパー メソッドを使用すると、これを行うことができます:


コードをコピーします
コードは次のとおりです: var パネル = $( "#myPanel" ); panel.firstClick(function() {
$.when(
$.get( "panel.html" ),
panel.slideDownPromise ()
).done(function( ajaxResponse ) {
panel.html( ajaxResponse[ 0 ] ).fadeIn();
});



画像をロードし、最初のクリックでパネルを開きます


コンテンツを含むパネルがすでにあるとしますが、それが必要なのは、ボタンがクリックされたときのみです初めてクリックすると、画像がロードされ、すべての画像が正常にロードされるとフェードインします。 HTML コードは次のとおりです。


コードをコピー

コードは次のとおりです。 < div id="myPanel" > data-src="image3 .png" />

data-src 属性でイメージの実パスを記述します。 このユースケースを解決するために Promise アシスタントを使用するコードは次のとおりです。




コードをコピーします

コードは次のとおりです。

$( "#myButton" ).firstClick(function() {
var panel = $( "#myPanel" ),
promises = [];
$( "img" , panel ).each(function() {
var image = $( this ), src = element.attr( "data-src" );
if ( src ) {
promises.push(
$.loadImage( src ).then( function() {
image.attr( "src", src );
}, function() {
image.attr( "src", " error.png" );
} )
);
}
});

promises.push( panel.slideDownPromise() );

$ .when.apply( null, promises ).done(function() { panel.fadeIn(); });
});

The trick here is to keep track of all LoadImage promises , then add the panel slideDown animation. So the first time the button is clicked, the panel will slideDown and the images will start loading. The panel will only fade in once you've finished sliding it down and all images have been loaded.

Load images on the page after a specific delay
Suppose, we want to implement deferred image display on the entire page. To do this, the format of the HTML we need is as follows:
Copy code The code is as follows:






The meaning is very simple:

•image1.png, the third image is displayed immediately , the first image will be displayed after one second
·image2.png, the second image will be displayed after one second, and the fourth image will be displayed after two seconds
How will we achieve this?

Copy code The code is as follows:

$( "img" ).each( function() {
var element = $( this ),
src = element.attr( "data-src" ),
after = element.attr( "data-after" );
if ( src ) {
$.when(
$.loadImage( src ),
$.afterDOMReady( after )
).then(function() {
element.attr( " src", src );
}, function() {
element.attr( "src", "error.png" );
} ).done(function() {
element. fadeIn();
});
}
});


If we want to lazy load the image itself, the code will be different:

Copy code The code is as follows:

$( "img" ).each(function() {
var element = $( this ),
src = element.attr( "data-src" ),
after = element.attr( "data-after" );
if ( src ) {
$.afterDOMReady( after, function() {
$.loadImage( src ).then(function() {
element.attr( "src", src );
}, function() {
element.attr( "src", "error.png" );
} ).done(function() {
element.fadeIn();
});
} );
}
});


Here, we first wait for the delay condition to be met before trying to load the image. This makes sense when you want to limit the number of network requests while a page is loading.

Conclusion
As you can see, promises are very useful even without Ajax requests. By using the deferred implementation in jQuery 1.5, it is very easy to separate asynchronous tasks from your code. This way, you can easily separate logic from your application.
関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート