日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

jQuery.Deferred和Promise

發(fā)布時間:2023/12/15 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 jQuery.Deferred和Promise 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

jQuery.Deferred和Promise

(轉(zhuǎn) 阮一峰)

引子

JS對無法及時得到結(jié)果的操作,一般都是進行異步操作,然后通過某些模式來得到結(jié)果。jQuery開始在版本1.5中提供了Deferred對象,這是一個基于 CommonJS Promises/A的設(shè)計,為了方便異步操作。在jQuery版本1.8中,使用Promise,可以采用promise對象提供操作。

概述

deferred對象代表了將要完成的某種操作,并提供了一些方法,幫助用戶使用。在早期(jQuery 1.5),它是jQuery對Promises接口的實現(xiàn)。jQuery的所有Ajax操作函數(shù),默認(rèn)返回的就是一個deferred對象。

簡單說,Promises是異步操作的通用接口,扮演代理人(proxy)的角色,將異步操作包裝成具有同步操作特性的特殊對象。異步操作的典型例子就是Ajax操作、網(wǎng)頁動畫、web worker等等。

由于JavaScript單線程的特點,如果某個操作耗時很長,其他操作就必需排隊等待。為了避免整個程序失去響應(yīng),通常的解決方法是將那些排在后面的操作,寫成“回調(diào)函數(shù)”(callback)的形式。這樣做雖然可以解決問題,但是有一些顯著缺點:


1. 回調(diào)函數(shù)往往寫成函數(shù)參數(shù)的形式,形成所謂的“持續(xù)傳遞風(fēng)格”(即參數(shù)就是下一步操作,Continuation-passing style),導(dǎo)致函數(shù)的輸入和輸出非常混亂,整個程序的可閱讀性差; 2. 回調(diào)函數(shù)往往只能指定一個,如果有多個操作,就需要改寫回調(diào)函數(shù)。除了正常的報錯機制,錯誤還可能通過回調(diào)函數(shù)的形式返回,增加了除錯和調(diào)試的難度。 3. 正常的函數(shù)輸入和輸出可以區(qū)分得很清楚,回調(diào)函數(shù)使得函數(shù)的輸出不再重要。

Promises就是為了解決這些問題而提出的,它的主要目的就是取代回調(diào)函數(shù),成為非同步操作的解決方案。它的核心思想就是讓非同步操作返回一個對象,其他操作都針對這個對象來完成。比如,假定ajax操作返回一個Promise對象。

// 偽碼 var promise = get('http://www.example.com');

然后,Promise對象有一個then方法,可以用來指定回調(diào)函數(shù)。一旦非同步操作完成,就調(diào)用指定的回調(diào)函數(shù)。

// 偽碼 promise.then(function (content) {console.log(content) })

可以將上面兩段代碼合并起來,這樣程序的流程看得更清楚。

// 偽碼 get('http://www.example.com').then(function (content) {console.log(content) })

在1.7版之前,jQuery的Ajax操作采用回調(diào)函數(shù)

$.ajax({url:"/echo/json/",success: function(response){console.info(response.name);}error:function(){console.error("error!");} });

如果高于1.5.0版本,返回的是deferred對象,可以進行鏈?zhǔn)讲僮鳌?/p>   $.ajax("test.html").done(function(){ alert("哈哈,成功了!"); }).fail(function(){ alert("出錯啦!"); });

這里done()相當(dāng)于success方法,fail()相當(dāng)于error方法。

在1.7版之后,Ajax操作直接返回Promise對象,這意味著可以用promise的then方法指定回調(diào)函數(shù)。

$.ajax({url: "/echo/json/"}) .then(function (response) {console.info(response.name); });

deferred對象及方法

在jQuery中,deferred對象就是jQuery的回調(diào)函數(shù)解決方案。其解決了如何處理耗時操作的問題,并對那些操作提供了更好的控制,以及統(tǒng)一的編程接口。

基本用法

  • 生成deferred對象
    第一步是通過$.Deferred()方法,生成一個deferred對象。
  • var deferred = $.Deferred();
  • deferred對象的狀態(tài)
  • deferred對象有三種狀態(tài)。

    pending:表示操作還沒有完成。 resolved:表示操作成功。 rejected:表示操作失敗。

    state方法用來返回deferred對象當(dāng)前狀態(tài)。

    $.Deferred().state() // 'pending' $.Deferred().resolve().state() // 'resolved' $.Deferred().reject().state() // 'rejected'
  • 改變狀態(tài)的方法
  • resolve方法將deferred對象的狀態(tài)從pending改為resolved,reject方法則將狀態(tài)從pending改為rejected。

    var deferred = $.Deferred();deferred.resolve("hello world");

    resolve方法的參數(shù),用來傳遞給回調(diào)函數(shù)。

  • 綁定回調(diào)函數(shù)
  • deferred對象在狀態(tài)改變時,會觸發(fā)回調(diào)函數(shù)。

    方法done指定狀態(tài)變?yōu)閞esolved(操作成功)時的回調(diào)函數(shù);方法fail 指定狀態(tài)變?yōu)閞ejected(操作失敗)時的回調(diào)函數(shù);方法***always方法指定,不管狀態(tài)變?yōu)閞esolved或rejected,都會觸發(fā)的方法。

    var deferred = $.Deferred();deferred.done(function(value) {console.log(value); }).resolve('hello world'); // hello world

    上述三種方法都返回的是原有的deferred對象,因此可以采用鏈?zhǔn)綄懛?#xff0c;在后面再鏈接別的方法(包括done和fail在內(nèi))。

    $.Deferred().done(f1).fail(f2).always(f3);

    使用方法notify() 和 progress()

    方法progress()用來指定一個回調(diào)函數(shù),當(dāng)調(diào)用notify()方法時,該回調(diào)函數(shù)將執(zhí)行。它的用意是提供一個接口,使得在非同步操作執(zhí)行過程中,可以執(zhí)行某些操作,比如定期返回進度條的進度,如下面的示例。

    var userProgress = $.Deferred();var $profileFields = $("input");var totalFields = $profileFields.lengthuserProgress.progress(function (filledFields) {var pctComplete = (filledFields/totalFields)*100;$("#progress").html(pctComplete.toFixed(0));}); userProgress.done(function () {$("#thanks").html("Thanks for completing your profile!").show();});$("input").on("change", function () {var filledFields = $profileFields.filter("[value!='']").length;userProgress.notify(filledFields);if (filledFields == totalFields) {userProgress.resolve();}});

    then方法

    概述

    方法then的作用也是指定回調(diào)函數(shù),它可以接受三個參數(shù),也就是三個回調(diào)函數(shù)。第一個參數(shù)是resolve時調(diào)用的回調(diào)函數(shù)(相當(dāng)于done方法),第二個參數(shù)是reject時調(diào)用的回調(diào)函數(shù)(相當(dāng)于fail方法),第三個參數(shù)是progress()方法調(diào)用的回調(diào)函數(shù)。

    deferred.then( doneFilter [, failFilter ] [, progressFilter ] )

    返回值

    在jQuery 1.8之前,then()只是.done().fail()寫法的語法糖,兩種寫法是等價的。在jQuery 1.8之后,then()返回一個新的promise對象,而done()返回的是原有的deferred對象。

    如果then()指定的回調(diào)函數(shù)有返回值,該返回值會作為參數(shù),傳入后面的回調(diào)函數(shù)。

    var defer = jQuery.Deferred();defer.done(function(a,b){return a * b; }).done(function( result ) {console.log("result = " + result); }).then(function( a, b ) {return a * b; }).done(function( result ) {console.log("result = " + result); }).then(function( a, b ) {return a * b; }).done(function( result ) {console.log("result = " + result); });defer.resolve( 2, 3 );

    在jQuery 1.8版本之前,上面代碼的結(jié)果是:

    result = 2 result = 2

    在jQuery 1.8版本之后,返回結(jié)果是

    result = 6 result = NaN

    結(jié)果是不同的,請注意。

    下面代碼最后那個done方法,處理的是從url2獲取的數(shù)據(jù),而不是從url1獲取的數(shù)據(jù)。 $.ajax( url1, { dataType: "json" } ) .then(function( data ) {return $.ajax( url2, { data: { user: data.userId } } ); }).done(function( data ) {// 從url2獲取的數(shù)據(jù) });

    對返回值的修改

    利用then()會修改返回值這個特性,我們可以在調(diào)用其他回調(diào)函數(shù)之前,對前一步操作返回的值進行處理。

    var post = $.post("/echo/json/").then(function(p){return p.firstName;});post.done(function(r){ console.log(r); });

    上面代碼先使用then()方法,從返回的數(shù)據(jù)中取出所需要的字段(firstName),所以后面的操作就可以只處理這個字段了。

    有時,Ajax操作返回json字符串里面有一個error屬性,表示發(fā)生錯誤。這個時候,傳統(tǒng)的方法只能是通過done()來判斷是否發(fā)生錯誤。通過then()方法,可以讓deferred對象調(diào)用fail()方法。

    var myDeferred = $.post('/echo/json/', {json:JSON.stringify({'error':true})}).then(function (response) {if (response.error) {return $.Deferred().reject(response);}return response;},function () {return $.Deferred().reject({error:true});});myDeferred.done(function (response) {$("#status").html("Success!");}).fail(function (response) {$("#status").html("An error occurred");});

    上面代碼中,不管是通信出錯,或者服務(wù)器返回一個錯誤,都會調(diào)用reject方法,返回一個新的deferred對象,狀態(tài)為rejected,因此就會觸發(fā)fail方法指定的回調(diào)函數(shù)。


    關(guān)于error的處理,jQuery的deferred對象與其他實現(xiàn)Promises規(guī)范的函數(shù)庫有一個重大不同。就是說,如果deferred對象執(zhí)行過程中,拋出一個非Promises對象的錯誤,那么將不會被后繼的then方法指定的rejected回調(diào)函數(shù)捕獲,而會一直傳播到應(yīng)用程序?qū)用妗榱舜a行為與Promises規(guī)范保持一致,建議出錯時,總是使用reject方法返回錯誤。

    d = $.Deferred() d.then(function(){ throw new Error('err') }).fail(function(){console.log('fail') }) d.resolve() // Error: err

    上面代碼中,then的回調(diào)函數(shù)拋出一個錯誤,按照Promises規(guī)范,應(yīng)該被fail方法的回調(diào)函數(shù)捕獲,但是jQuery的部署是上升到應(yīng)用程序的層面。

    回調(diào)函數(shù)的返回值

    如果回調(diào)函數(shù)返回deferred對象,則then方法的返回值將是對應(yīng)這個返回值的promise對象。

    var d1 = $.Deferred();var promise = $.when('Hello').then(function(h){ return $.when(h,d1); })promise.done(function (s1,s2) {console.log(s1);console.log(s2); })d1.resolve('World') // Hello // World

    上面代碼中,done方法的回調(diào)函數(shù),正常情況下只能接受一個參數(shù)。但是由于then方法的回調(diào)函數(shù),返回一個when方法生成的deferred對象,導(dǎo)致它可以接受兩個參數(shù)。

    pipe方法

    pipe方法接受一個函數(shù)作為參數(shù),表示在調(diào)用then方法、done方法、fail方法、always方法指定的回調(diào)函數(shù)之,先運行pipe方法指定的回調(diào)函數(shù)。它通常用來對服務(wù)器返回的數(shù)據(jù)做初步處理。

    與Promise A+規(guī)格的差異

    Promise事實上的標(biāo)準(zhǔn)是社區(qū)提出的Promise A+規(guī)格,jQuery的實現(xiàn)并不完全符合Promise A+,主要是對錯誤的處理。

    var promise2 = promise1.then(function () {throw new Error("boom!"); });

    上面代碼在回調(diào)函數(shù)中拋出一個錯誤,Promise A+規(guī)定此時Promise實例的狀態(tài)變?yōu)閞eject,該錯誤被下一個catch方法指定的回調(diào)函數(shù)捕獲。但是,jQuery的Deferred對象此時不會改變狀態(tài),亦不會觸發(fā)回調(diào)函數(shù),該錯誤一般情況下會被window.onerror捕獲。換句話說,在Deferred對象中,總是必須使用reject方法來改變狀態(tài)。

    promise對象

    概念

    一般情況下,從外部改變第三方完成的異步操作(比如Ajax)的狀態(tài)是毫無意義的。為了防止用戶這樣做,可以在deferred對象的基礎(chǔ)上,返回一個針對它的promise對象。

    簡單說,promise對象就是不能改變狀態(tài)的deferred對象,也就是deferred的只讀版。或者更通俗地理解成,promise是一個對將要完成的任務(wù)的承諾,排除了其他人破壞這個承諾的可能性,只能等待承諾方給出結(jié)果。

    你可以通過promise對象,為原始的deferred對象添加回調(diào)函數(shù),查詢它的狀態(tài),但是無法改變它的狀態(tài),也就是說promise對象不允許你調(diào)用resolve和reject方法。

    生成promise對象

    deferred對象的promise方法,用來生成對應(yīng)的promise對象。
    $.Deferred().promise()

    示例:

    function getPromise(){return $.Deferred().promise(); }try{getPromise().resolve("a");// 無法使用 } catch(err) {console.log(err); } // TypeError

    上面代碼對promise對象,調(diào)用resolve方法,結(jié)果報錯。

    jQuery的ajax()方法返回的就是一個Promise對象。此外,Animation類操作也可以使用promise方法。

    $('body').toggle('blinds').promise().then(function(){$('body').toggle('blinds')} )

    輔助方法

    deferred對象還有一系列輔助方法,使它更方便使用。

    $.when()方法

    $.when()接受多個deferred對象作為參數(shù),當(dāng)它們?nèi)窟\行成功后,才調(diào)用resolved狀態(tài)的回調(diào)函數(shù),但只要其中有一個失敗,就調(diào)用rejected狀態(tài)的回調(diào)函數(shù)。它相當(dāng)于將多個非同步操作,合并成一個。實質(zhì)上,when方法為多個deferred對象,返回一個單一的promise對象。

    $.when($.ajax( "/main.php" ),$.ajax( "/modules.php" ),$.ajax( "/lists.php" ) ).then(successFunc, failureFunc);

    上面代碼表示,要等到三個ajax操作都結(jié)束以后,才執(zhí)行then方法指定的回調(diào)函數(shù)。

    when方法里面要執(zhí)行多少個操作,回調(diào)函數(shù)就有多少個參數(shù),對應(yīng)前面每一個操作的返回結(jié)果。

    $.when($.ajax( "/main.php" ),$.ajax( "/modules.php" ),$.ajax( "/lists.php" ) ).then(function (resp1, resp2, resp3){console.log(resp1);console.log(resp2);console.log(resp3); });

    上面代碼的回調(diào)函數(shù)有三個參數(shù),resp1、resp2和resp3,依次對應(yīng)前面三個ajax操作的返回結(jié)果。

    如果when方法的參數(shù)不是deferred或promise對象,則直接作為回調(diào)函數(shù)的參數(shù)。

    d = $.Deferred() $.when(d, 'World').done(function (s1, s2){console.log(s1);console.log(s2); })d.resolve('Hello') // Hello // World

    上面代碼中,when的第二個參數(shù)是一個字符串,則直接作為回調(diào)函數(shù)的第二個參數(shù)。

    在看一下,下面不會規(guī)范的寫法:

    d = $.Deferred() $.when("first", d, 'World').done(function (s1, s2, s3){console.log(s1);console.log(s2);console.log(s3); })d.resolve('Hello') // first // Hello // World

    此外,如果when方法的參數(shù)都不是deferred或promise對象,那么when方法的回調(diào)函數(shù)將立即運行。

    使用實例

    wait方法

    我們可以用deferred對象寫一個wait方法,表示等待多少毫秒后再執(zhí)行。

    $.wait = function(time) {return $.Deferred(function(dfd) {setTimeout(dfd.resolve, time);}); }

    使用方法如下:

    $.wait(5000).then(function() {console.log("Hello from the future!"); });

    改寫setTimeout

    在上面的wait方法的基礎(chǔ)上,還可以改寫setTimeout方法,讓其返回一個deferred對象。

    function doSomethingLater(fn, time) {var dfd = $.Deferred();setTimeout(function() {dfd.resolve(fn());}, time || 0);return dfd.promise(); }

    使用

    var promise = doSomethingLater(function (){console.log( '已經(jīng)延遲執(zhí)行' ); }, 100);

    自定義操作使用deferred接口

    我們可以利用deferred接口,使得任意操作都可以用done()和fail()指定回調(diào)函數(shù)。

    Twitter = {search:function(query) {var dfd = $.Deferred();$.ajax({url:"http://search.twitter.com/search.json",data:{q:query},dataType:'jsonp',success:dfd.resolve});return dfd.promise();} }

    使用方法如下

    Twitter.search('javaScript').then(function(data) {alert(data.results[0].text); });

    deferred對象的另一個優(yōu)勢是可以附加多個回調(diào)函數(shù)。下面的例子使用了上面所改寫的setTimeout函數(shù)。

    function doSomething(arg) {var dfd = $.Deferred();setTimeout(function() {dfd.reject("Sorry, something went wrong.");});return dfd; } doSomething("uh oh").done(function() {console.log("Won't happen, we're erroring here!"); }).fail(function(message) {console.log(message); });

    總結(jié)

    以上是生活随笔為你收集整理的jQuery.Deferred和Promise的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。