本地址 http://www.cnblogs.com/wangfupeng1988/p/6515779.html 未經容許不得轉載~html
$.ajax
這個函數各位應該都比較熟悉了,要完整的講解 js 的異步操做,就必須先從$.ajax
這個方法提及。前端
想要學到全面的知識,你們就不要着急,跟隨個人節奏來,而且相信我。我安排的內容,確定都是有用的,對主題無用的東西,我不會拿來佔用你們的時間。jquery
$.ajax
$.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
$.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
對象,這個對象就帶有done
和fail
的方法,而且是等着請求返回以後再去調用這是一個標誌性的改造,無論這個概念是誰最早提出的,它在 jquery 中首先大量使用並讓全球開發者都知道原來 ajax 請求還能夠這樣寫。這爲之後的Promise
標準制定提供了很大意義的參考,你能夠覺得這就是後面Promise
的原型。ajax
記住一句話————雖然 JS 是異步執行的語言,可是人的思惟是同步的————所以,開發者老是在尋求如何使用邏輯上看似同步的代碼來完成 JS 的異步請求。而 jquery 的這一次更新,讓開發者在必定程度上獲得了這樣的好處。json
以前不管是什麼操做,我都須要一股腦寫到callback
中,如今不用了。如今成功了就寫到done
中,失敗了就寫到fail
中,若是成功了有多個步驟的操做,那我就寫不少個done
,而後鏈式鏈接起來就 OK 了。promise
Promise
的關係以上的這段代碼,咱們還能夠這樣寫。即不用done
和fail
函數,而是用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 v1.5 版本開始,$.ajax
可使用相似當前Promise
的then
函數以及鏈式操做。那麼它究竟是如何實現的呢?在此以前所用到的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
fail
和then
方法(不明白的去看上一節)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
fail
和then
方法(上一節說過),它還有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 的deferred
對象是解決了異步中callback
函數的問題,可是
本節使用的代碼參見這裏
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 dtd
。dtd
是一個deferred
對象,而dtd.promise
就是一個promise
對象。
promise
對象和deferred
對象最重要的區別,記住了————promise
對象相比於deferred
對象,缺乏了.resolve
和.reject
這倆函數屬性。這麼一來,可就徹底不同了。
上一節咱們提到一個問題,就是在程序的最後一行加一句w.reject()
會致使亂套,你如今再在最後一行加w.reject()
試試 ———— 保證亂套不了 ———— 而是你的程序不能執行,直接報錯。由於,w
是promise
對象,不具有.reject
屬性。
promise
的好處上一節提到deferred
對象有兩組屬性函數,並且提到應該把這兩組完全分開。如今經過上面一行代碼的改動,就分開了。
waitHandle
函數內部,使用dtd.resolve()
來該表狀態,作主動的修改操做waitHandle
最終返回promise
對象,只能去被動監聽變化(then
函數),而不能去主動修改操做一個「主動」一個「被動」,徹底分開了。
jquery v1.5 版本發佈時間距離如今(2017年初春)已經老早以前了,那會兒你們網頁標配都是 jquery 。不管裏面的deferred
和promise
這個概念和想法最先是哪位提出來的,可是最先展現給全世界開發者的是 jquery ,這算是Promise
這一律念最早的提出者。
其實本次課程主要是給你們分析 ES6 的Promise
Generator
和async-await
,可是爲什麼要從 jquery 開始(你們如今用 jquery 愈來愈少)?就是要給你們展現一下這段歷史的一些起點和發展的知識。有了這些基礎,你再去接受最新的概念會很是容易,由於全部的東西都是從最初順其天然發展進化而來的,咱們要去用一個發展進化的眼光學習知識,而不是死記硬背。
若是你看完了,感受還不錯,歡迎給我打賞 ———— 以激勵我更多輸出優質內容
最後,github地址是 https://github.com/wangfupeng1988/js-async-tutorial 歡迎 star 和 pr
-------
學習做者教程:《前端JS高級面試》《前端JS基礎面試題》《React.js模擬大衆點評webapp》《zepto設計與源碼分析》《json2.js源碼解讀》