1. 程式人生 > >jQuery回撥、遞延物件總結(中篇) —— 神奇的then方法

jQuery回撥、遞延物件總結(中篇) —— 神奇的then方法

前言:

什麼叫做遞延物件,生成一個遞延物件只需呼叫jQuery.Deferred函式,deferred這個單詞譯為延期,推遲,即延遲的意思,那麼在jQuery中

又是如何表達延遲的呢,從遞延物件中的then方法或許能找到這種延遲的行為,本文重點解讀遞延物件中的then方法

jQuery回撥、遞延物件總結篇索引:

設計思路:

在遞延物件構造中,分別有三組回撥物件,每一組回撥物件都有與之對應的行為(action,add listener),和狀態(final state),

這些行為都歸納為遞延物件中的(觸發回撥,新增函式到回撥列表中等)方法

jQuery.Deferred構造原始碼

Deferred構造原始碼除了then函式原始碼外,其他都非常簡單,這裡不做過多解讀,後面將重點討論then方法

jQuery.extend({

    Deferred: function( func ) {
        var tuples = [
                // action, add listener, listener list, final state
                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
                [ 
"reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], state = "pending", promise = { state: function() { return state; }, always:
function() { deferred.done( arguments ).fail( arguments ); return this; }, then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var action = tuple[ 0 ], fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[1] ](function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else { newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); fns = null; }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Keep pipe for back-compat promise.pipe = promise.then; // Add list-specific methods jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add; // Handle state if ( stateString ) { list.add(function() { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock // 可以看看上篇中lock方法的各種場景呼叫 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function() { // 如果方法不被借用,那麼回撥中的this物件為promise,沒有觸發回撥的方法 deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith; }); // Make the deferred a promise promise.promise( deferred ); // Call given func if any // 作用於then方法 if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; } });
View Code

神奇的then方法

在實際專案應用中,一個頁面或許有多個ajax請求,你可能會這樣做:

$.ajax({ url1, ... });
$.ajax({ url2, ... });
$.ajax({ url3, ... });
...

這樣做的缺點:

1、多個ajax同時傳送請求,可能會造成伺服器壓力,對於富應用頁面來說,如果請求過多,那是必然的;

2、對於頁面底部,或者說首屏不展示給使用者瀏覽的部分需要傳送的ajax請求,沒有必要讓它一開始載入頁面後就傳送請求,這樣會造成頁面響應緩慢

jQuery遞延物件中的then方法好像天生就是為了解決以上問題而設計的,它可以按照順序依次處理多個非同步請求,即第一個請求處理完後,

再處理第二個請求,以此類推,這樣既可以減輕伺服器壓力,又可以先發送首屏(從上到下)頁面部分的請求,使頁面響應更快

來看看一段非常優雅的例項程式碼

var promiseA = $.get(urlA);
promiseA.always(doneFnA, failFnA, progressFnA);

var promiseB = promiseA.then(function(){
    return $.get(urlB);
});
promiseB.always(doneFnB, failFnB, progressFnB);

或者你也可以這樣寫,但並不建議:

var promiseB = $.get(urlA).then(function(){
    var state = this.state();
    // 針對第一個ajax請求的處理
    switch (state) {
        case 'resolved' :
            doneFnA();
            break;
        case 'rejected' :
            failFnA();
            break;
        case 'pending' :
            progressA();
            break;
        default: 
            break;
    }
    return $.get(urlB);
});
promiseB.always(doneFnB, failFnB, progressB);
View Code


上面程式碼是如何執行的呢:

首先發送第一個ajax請求,當promiseA物件執行過resolve(或reject、notify)後,即:第一個請求成功或失敗後,將依次執行回撥doneFnA

(或failFnA、progressFnA),then中的匿名函式(注意程式碼的順序,之前程式碼順序有誤,把promiseA.always放在了then方法執行之後,現已改過來了),

匿名函式中傳送第二個ajax請求,當請求成功或失敗後,將執行對應的回撥函式(doneFnB或failFnB、progressFnB)

衍生後的程式碼

var promiseA = $.get(urlA);
// 這裡新增promiseA的回撥

var promiseB = promiseA.then(function(){
    return $.get(urlB);
});
// 這裡新增promiseB的回撥

var promiseC = promiseB.then(function(){
    return $.get(urlC);
});
// 這裡新增promiseC的回撥

var promiseD = promiseC.then(function(){
    return $.get(urlD);
});
// 這裡新增promiseD的回撥

再來看看then函式中的構造原始碼,通過上面的例項分析,相信眼前的你會恍然大悟的

promise = {
    then: function( /* fnDone, fnFail, fnProgress */ ) {
        var fns = arguments;
        // 返回後的promise物件與newDefer對應
        return jQuery.Deferred(function( newDefer ) {
            jQuery.each( tuples, function( i, tuple ) {
                var action = tuple[ 0 ],
                    fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
                // deferred[ done | fail | progress ] for forwarding actions to newDefer
                // 為第一個遞延物件添加回調
                deferred[ tuple[1] ](function() {
                    var returned = fn && fn.apply( this, arguments );

                    // 如果回撥返回的是一個遞延物件,newDefer將根據這個返回的遞延物件的狀態來觸發行為
                    if ( returned && jQuery.isFunction( returned.promise ) ) {   
                        
                        returned.promise()
                            .done( newDefer.resolve )
                            .fail( newDefer.reject )
                            .progress( newDefer.notify );
                    }
                    // 如果回撥返回的不是一個遞延物件,newDefer將根據第一個(deferred)遞延物件的狀態來觸發行為
                    else {
                        newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
                    }
                });
            });
            fns = null;
        }).promise();
    }
}

PS: 如有描述錯誤,請幫忙指正,如果你們有不明白的地方也可以發郵件給我,

  如需轉載,請附上本文地址及出處:部落格園華子yjh,謝謝!

相關推薦

jQuery物件總結中篇) —— 神奇then方法

前言: 什麼叫做遞延物件,生成一個遞延物件只需呼叫jQuery.Deferred函式,deferred這個單詞譯為延期,推遲,即延遲的意思,那麼在jQuery中 又是如何表達延遲的呢,從遞延物件中的then方法或許能找到這種延遲的行為,本文重點解讀遞延物件中的then方法 jQuery回撥、遞延物件

jQuery物件總結上篇)—— jQuery.Callbacks

前言: 作為引數傳遞給另一個函式執行的函式我們稱為回撥函式,那麼該回調又是否是非同步的呢,何謂非同步,如:作為事件處理器,或作為引數傳遞給 (setTimeout,setInterval)這樣的非同步函式,或作為ajax傳送請求,應用於請求各種狀態的處理,我們可以稱為非同步回撥,jQuery.Callba

jQuery物件總結下篇) —— 解密jQuery.when方法

前言: 前一篇文章中重點總結了一下then方法,它主要用來處理多個非同步任務按順序執行,即前一個任務處理完了,再繼續下一個,以此類推; 而這一章節jQuery.when方法也是處理多個非同步任務,它把多個非同步任務(Promise物件)合併為一個Promise物件,這個合併後的Promise物件 到底是

js進階四(jspromisepromise巢狀異常處理jquery使用promise)

同步讀取 我們來看一個從檔案讀取內容的例子,以下是這個例子的目錄結構 我們看下promiser.js的程式碼如下: const fs = require("fs") const path = require("path") function getFile

PHP之匿名函式與閉包

回撥函式:通俗的解釋就是把函式作為引數傳入進另一個函式中使用;PHP中有許“需求引數為函式”的函式,像array_map,usort,call_user_func_array之類,他們執行傳入的函式,然後直接將結果返回主函式。好處是函式作為值使用起來方便,而且程式碼簡潔,可讀

前端面試——PromiseGenerator和async-await

首先我們回顧一下javascript非同步的發展歷程。 ES6 以前:   回撥函式(callback):nodejs express 中常用,ajax中常用。 ES6:   promise物件:nodejs最早有bluebird promise的雛形,axios中常

非同步事件驅動協程概念辨析

同步和非同步: 面試問題什麼是非同步非阻塞 A. 同步 所謂同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。 B. 非同步 非同步的概念和同步相對。 當一個非同步過程呼叫發出後,先返回,呼叫者不會立刻得到結果。 實際處理這個呼叫的部件是在呼叫發出後, 通過狀態、通知來通知

Android Activity的onStop()與onDestroy() 緩慢,時呼叫的問題解決方案

前段時間做專案時遇到奇葩問題,特此記錄: 問題發現: 我們的專案在語句翻譯功能裡用到了百度語音識別和語音合成,把相關程式碼封裝到了library裡面,把library庫放到專案A裡面執行正常,同樣的庫移植到專案B裡面,居然有問題!!! 具體問題就是第一次進入Activity時正常,但是當退出

Java中的單例模式工廠模式介面異常

for迴圈:起點為基本資料型別,包括boolean . equals():重寫原因,希望在地址不同但內容相同時也能返回true。 匿名物件:直接new出物件,不需要物件名來接收。 new Person().show(); 內部類:類

Java程式設計之委託代理內部類以及匿名內部類(閉包)

最近一直在看Java的相關東西,因為我們在iOS開發是,無論是Objective-C還是Swift中,經常會用到委託代理回撥,以及Block回撥或者說是閉包回撥。接下來我們就來看看Java語言中是如何實現委託代理回撥以及閉包回撥的。當然這兩個技術點雖然實現起來並不困難,但是,這回調在封裝一些公用元件時還是特別

java 三種呼叫機制同步非同步)

java中存在三種呼叫機制 1:同步呼叫:一種阻塞式呼叫,呼叫方要等待對方執行完畢才返回,它是一種單向呼叫 2:回撥:一種雙向呼叫模式,也就是說,被呼叫方在介面被呼叫時也會呼叫對方的介面; 3:非同步呼叫:一種類似訊息或事件的機制,不過它的呼叫方向剛好相反

JavaScript踩坑筆記10---同步非同步

JavaScript踩坑筆記10---同步回撥、非同步回撥 同步回撥: 非同步回撥: 同步回撥: 同步回撥指的是,回撥函式和主函式的執行是同步的,回撥函式在主函式內執行,並且主函式要等回撥函式執行完畢以後,才能執行。 舉例說明。 // 定

Android中介面方法

在android開發中我們很多地方都用到了方法的回撥,回撥就是把方法的定義和功能匯入實現分開的一種機制,目的是為了解耦他的本質是基於觀察者設計模式,即觀察者設計模式的的簡化版,例如:在下載時候的進度回撥,在adapter與activity之間的回撥,在javabean和fr

20.php匿名函式和閉包操作

    現在的php是即支援面向物件的語言由支援面向過程的語言,在開發過程中我們往往會混合使用,回撥會讓我們容易將兩種編碼方式做整合,做到優秀的插拔。而閉包操作和回撥都是建立在匿名函式基礎之上的。 &

iOS整合微信支付的一些坑:onResp不只顯示一個確定按鈕

iOS整合微信支付總體來說還是比較容易的(如果沒有那些坑的話),所有文件都在:    https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_1甚至只要看:    https://pay.weixin.qq.com/w

關於訊息事件【轉】

二,回撥函式、訊息和事件例程                        呼叫(calling)機制從彙編時代起已經大量使用:準備一段現成的程式碼,呼叫者可以隨時跳轉至此段程式碼的起始地址,執行完後再返回跳轉時的後續地址。CPU為此準備了現成的呼叫指令,呼叫時可以壓棧保護現場,呼叫結束後從堆疊中彈出現場地址

面向物件總結php)

文章來自:原始碼線上https://www.shengli.me/php/59.html;    一、面向物件有一下幾個特性:   重用性 ——每個物件模組都可以在專案中重複使用。 拓展性——模組上新增新功能是很方便的. 靈活

javascript--函式基礎函式的定義/作用域,函式,即時函式,內部私有)函式,返回函式的函式,重寫自己的函式)

函式源於數學對映運算,它定義了一種關係,這種關係使一個集合裡的每一個元素對應到另一個(可能相同的)集合裡的唯一元素 javascript中: 函式是程式碼塊,一段被封閉嚴實的程式碼塊 函式是資料:使用者可以把函式作為 值 賦值給 變數 函式是一種物件,它是一類抽象類(建構函式),所有

setprecisionfixedshowpoint的用法總結經典!!超經典!!)

首先要加標頭檔案:iomanip 一:setprecision   作用:控制輸出流顯示浮點數的數字個數,setprecision(n)就是輸出的n個數,會有四捨五入。 比如:double s=20.7843000, cout<<setprecisio

php5 面向物件總結類與物件

1. PHP 類與物件  2. PHP 類的繼承 extends 關鍵字  3. PHP 構造方法 __construct()  4. PHP 析構方法 __destruct()  5. PHP final 關鍵字  6. PHP 類的介面 interface 與 imp