深刻理解 JavaScript 異步系列(2)—— jquery的解決方案

第一部分,jQuery-1.5 以後的 ajax

本地址 http://www.cnblogs.com/wangfupeng1988/p/6515779.html 未經容許不得轉載~html

$.ajax這個函數各位應該都比較熟悉了,要完整的講解 js 的異步操做,就必須先從$.ajax這個方法提及。前端

想要學到全面的知識,你們就不要着急,跟隨個人節奏來,而且相信我。我安排的內容,確定都是有用的,對主題無用的東西,我不會拿來佔用你們的時間。jquery

本節內容概述

  • 傳統的$.ajax
  • 1.5 版本以後的$.ajax
  • 改進以後的好處
  • 和後來的Promise的關係
  • 如何實現的?

傳統的$.ajax

先來一段最多見的$.ajax的代碼,固然是使用萬惡的callback方式git

var ajax = $.ajax({
    url: 'data.json',
    success: function () {
        console.log('success')
    },
    error: function () {
        console.log('error')
    }
})

console.log(ajax) // 返回一個 XHR 對象

至於這麼作會產生什麼樣子的詬病,我想你們應該都很明白了。不明白的本身私下去查,可是你也能夠繼續往下看,你只須要記住這樣作很很差就是了,要否則 jquery 也不會再後面進行改進github

1.5 版本以後的$.ajax

可是從v1.5開始,以上代碼就能夠這樣寫了:能夠鏈式的執行done或者fail方法web

var ajax = $.ajax('data.json')
ajax.done(function () {
        console.log('success 1')
    })
    .fail(function () {
        console.log('error')
    })
    .done(function () {
         console.log('success 2')
    })

console.log(ajax) // 返回一個 deferred 對象

你們注意看以上兩段代碼中都有一個console.log(ajax),可是返回值是徹底不同的。面試

  • v1.5以前,返回的是一個XHR對象,這個對象不可能有done或者fail的方法的
  • v1.5開始,返回一個deferred對象,這個對象就帶有donefail的方法,而且是等着請求返回以後再去調用

改進以後的好處

這是一個標誌性的改造,無論這個概念是誰最早提出的,它在 jquery 中首先大量使用並讓全球開發者都知道原來 ajax 請求還能夠這樣寫。這爲之後的Promise標準制定提供了很大意義的參考,你能夠覺得這就是後面Promise的原型。ajax

記住一句話————雖然 JS 是異步執行的語言,可是人的思惟是同步的————所以,開發者老是在尋求如何使用邏輯上看似同步的代碼來完成 JS 的異步請求。而 jquery 的這一次更新,讓開發者在必定程度上獲得了這樣的好處。json

以前不管是什麼操做,我都須要一股腦寫到callback中,如今不用了。如今成功了就寫到done中,失敗了就寫到fail中,若是成功了有多個步驟的操做,那我就寫不少個done,而後鏈式鏈接起來就 OK 了。promise

和後來的Promise的關係

以上的這段代碼,咱們還能夠這樣寫。即不用donefail函數,而是用then函數。then函數的第一個參數是成功以後執行的函數(即以前的done),第二個參數是失敗以後執行的函數(即以前的fail)。並且then函數還能夠鏈式鏈接。

var ajax = $.ajax('data.json')
ajax.then(function () {
        console.log('success 1')
    }, function () {
        console.log('error 1')
    })
    .then(function () {
        console.log('success 2')
    }, function () {
        console.log('error 2')
    })

若是你對如今 ES6 的Promise有了解,應該能看出其中的類似之處。不瞭解也不要緊,你只須要知道它已經和Promise比較接近了。後面立刻會去講Promise

如何實現的?

明眼人都知道,jquery 不可能改變異步操做須要callback的本質,它只不過是本身定義了一些特殊的 API,並對異步操做的callback進行了封裝而已。

那麼 jquery 是如何實現這一步的呢?請聽下回分解!

 

第二部分,jQuery deferred

上一節講到 jquery v1.5 版本開始,$.ajax可使用相似當前Promisethen函數以及鏈式操做。那麼它究竟是如何實現的呢?在此以前所用到的callback在這其中又起到了什麼做用?本節給出答案

本節使用的代碼參見這裏

本節內容概述

  • 寫一個傳統的異步操做
  • 使用$.Deferred封裝
  • 應用then方法
  • 有什麼問題?

寫一個傳統的異步操做

給出一段很是簡單的異步操做代碼,使用setTimeout函數。

var wait = function () {
    var task = function () {
        console.log('執行完成')
    }
    setTimeout(task, 2000)
}
wait()

以上這些代碼執行的結果你們應該都比較明確了,即 2s 以後打印出執行完成可是我若是再加一個需求 ———— 要在執行完成以後進行某些特別複雜的操做,代碼可能會不少,並且分好幾個步驟 ———— 那該怎麼辦? 你們思考一下!

若是你不看下面的內容,並且目前尚未Promise的這個思惟,那估計你會說:直接在task函數中寫就是了!不過相信你看完下面的內容以後,會放棄你如今的想法。

使用$.Deferred封裝

好,接下來咱們讓剛纔簡單的幾行代碼變得更加複雜。爲什麼要變得更加複雜?是由於讓之後更加複雜的地方變得簡單。這裏咱們使用了 jquery 的$.Deferred,至於這個是個什麼鬼,你們先不用關心,只須要知道$.Deferred()會返回一個deferred對象,先看代碼,deferred對象的做用咱們會面會說。

function waitHandle() {
    var dtd = $.Deferred()  // 建立一個 deferred 對象

    var wait = function (dtd) {  // 要求傳入一個 deferred 對象
        var task = function () {
            console.log('執行完成')
            dtd.resolve()  // 表示異步任務已經完成
        }
        setTimeout(task, 2000)
        return dtd  // 要求返回 deferred 對象
    }

    // 注意,這裏必定要有返回值
    return wait(dtd)
}

以上代碼中,又使用一個waitHandle方法對wait方法進行再次的封裝。waitHandle內部代碼,咱們分步驟來分析。跟着個人節奏慢慢來,保證你不會亂。

  • 使用var dtd = $.Deferred()建立deferred對象。經過上一節咱們知道,一個deferred對象會有done failthen方法(不明白的去看上一節)
  • 從新定義wait函數,可是:第一,要傳入一個deferred對象(dtd參數);第二,當task函數(即callback)執行完成以後,要執行dtd.resolve()告訴傳入的deferred對象,革命已經成功。第三;將這個deferred對象返回。
  • 返回wait(dtd)的執行結果。由於wait函數中返回的是一個deferred對象(dtd參數),所以wait(dtd)返回的就是dtd————若是你感受這裏很亂,不要緊,慢慢捋,一行一行看,相信兩三分鐘就能捋順!

最後總結一下,waitHandle函數最終return wait(dtd)即最終返回dtd(一個deferred)對象。針對一個deferred對象,它有done failthen方法(上一節說過),它還有resolve()方法(其實和resolve相對的還有一個reject方法,後面會提到)

應用then方法

接着上面的代碼繼續寫

var w = waitHandle()
w.then(function () {
    console.log('ok 1')
}, function () {
    console.log('err 1')
}).then(function () {
    console.log('ok 2')
}, function () {
    console.log('err 2')
})

上面已經說過,waitHandle函數最終返回一個deferred對象,而deferred對象具備done fail then方法,如今咱們正在使用的是then方法。至於then方法的做用,咱們上一節已經講過了,不明白的同窗抓緊回去補課。

執行這段代碼,咱們打印出來如下結果。能夠將結果對標如下代碼時哪一行。

執行完成
ok 1
ok 2

此時,你再回頭想一想我剛纔說提出的需求(要在執行完成以後進行某些特別複雜的操做,代碼可能會不少,並且分好幾個步驟),是否是有更好的解決方案了?

有同窗確定發現了,代碼中console.log('err 1')console.log('err 2')何時會執行呢 ———— 你本身把waitHandle函數中的dtd.resolve()改爲dtd.reject()試一下就知道了。

  • dtd.resolve() 表示革命已經成功,會觸發then中第一個參數(函數)的執行,
  • dtd.reject() 表示革命失敗了,會觸發then中第二個參數(函數)執行

有什麼問題?

總結一下一個deferred對象具備的函數屬性,並分爲兩組:

  • dtd.resolve dtd.reject
  • dtd.then dtd.done dtd.fail

我爲什麼要分紅兩組 ———— 這兩組函數,從設計到執行以後的效果是徹底不同的。第一組是主動觸發用來改變狀態(成功或者失敗),第二組是狀態變化以後纔會觸發的監聽函數。

既然是徹底不一樣的兩組函數,就應該完全的分開,不然很容易出現問題。例如,你在剛纔執行代碼的最後加上這麼一行試試。

w.reject()

那麼如何解決這一個問題?請聽下回分解!

 

第三部分,jQuery promise

上一節經過一些代碼演示,知道了 jquery 的deferred對象是解決了異步中callback函數的問題,可是

本節使用的代碼參見這裏

本節內容概述

  • 返回promise
  • 返回promise的好處
  • promise 的概念

返回promise

咱們對上一節的的代碼作一點小小的改動,只改動了一行,下面註釋。

function waitHandle() {
    var dtd = $.Deferred()
    var wait = function (dtd) {
        var task = function () {
            console.log('執行完成')
            dtd.resolve()
        }
        setTimeout(task, 2000)
        return dtd.promise()  // 注意,這裏返回的是 primise 而不是直接返回 deferred 對象
    }
    return wait(dtd)
}

var w = waitHandle() // 通過上面的改動,w 接收的就是一個 promise 對象
$.when(w)
 .then(function () {
    console.log('ok 1')
 })
 .then(function () {
    console.log('ok 2')
 })

改動的一行在這裏return dtd.promise(),以前是return dtddtd是一個deferred對象,而dtd.promise就是一個promise對象。

promise對象和deferred對象最重要的區別,記住了————promise對象相比於deferred對象,缺乏了.resolve.reject這倆函數屬性。這麼一來,可就徹底不同了。

上一節咱們提到一個問題,就是在程序的最後一行加一句w.reject()會致使亂套,你如今再在最後一行加w.reject()試試 ———— 保證亂套不了 ———— 而是你的程序不能執行,直接報錯。由於,wpromise對象,不具有.reject屬性。

返回promise的好處

上一節提到deferred對象有兩組屬性函數,並且提到應該把這兩組完全分開。如今經過上面一行代碼的改動,就分開了。

  • waitHandle函數內部,使用dtd.resolve()來該表狀態,作主動的修改操做
  • waitHandle最終返回promise對象,只能去被動監聽變化(then函數),而不能去主動修改操做

一個「主動」一個「被動」,徹底分開了。

promise 的概念

jquery v1.5 版本發佈時間距離如今(2017年初春)已經老早以前了,那會兒你們網頁標配都是 jquery 。不管裏面的deferredpromise這個概念和想法最先是哪位提出來的,可是最先展現給全世界開發者的是 jquery ,這算是Promise這一律念最早的提出者。

其實本次課程主要是給你們分析 ES6 的Promise Generatorasync-await,可是爲什麼要從 jquery 開始(你們如今用 jquery 愈來愈少)?就是要給你們展現一下這段歷史的一些起點和發展的知識。有了這些基礎,你再去接受最新的概念會很是容易,由於全部的東西都是從最初順其天然發展進化而來的,咱們要去用一個發展進化的眼光學習知識,而不是死記硬背。

求打賞

若是你看完了,感受還不錯,歡迎給我打賞 ———— 以激勵我更多輸出優質內容

最後,github地址是 https://github.com/wangfupeng1988/js-async-tutorial 歡迎 star 和 pr

-------

學習做者教程:《前端JS高級面試》《前端JS基礎面試題》《React.js模擬大衆點評webapp》《zepto設計與源碼分析》《json2.js源碼解讀

相關文章
相關標籤/搜索