本地址 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源码解读》