深入理解 JavaScript 异步系列(2)—— jquery的解决方案
2017-04-07 22:22
543 查看
第一部分,jQuery-1.5 之后的 ajax
本地址 http://www.cnblogs.com/wangfupeng1988/p/6515779.html 未经允许不得转载~$.ajax这个函数各位应该都比较熟悉了,要完整的讲解 js 的异步操作,就必须先从
$.ajax这个方法说起。
想要学到全面的知识,大家就不要着急,跟随我的节奏来,并且相信我。我安排的内容,肯定都是有用的,对主题无用的东西,我不会拿来占用大家的时间。
本节内容概述
传统的$.ajax
1.5 版本之后的
$.ajax
改进之后的好处
和后来的
Promise的关系
如何实现的?
传统的$.ajax
先来一段最常见的$.ajax的代码,当然是使用万恶的
callback方式
var ajax = $.ajax({ url: 'data.json', success: function () { console.log('success') }, error: function () { console.log('error') } }) console.log(ajax) // 返回一个 XHR 对象
至于这么做会产生什么样子的诟病,我想大家应该都很明白了。不明白的自己私下去查,但是你也可以继续往下看,你只需要记住这样做很不好就是了,要不然 jquery 也不会再后面进行改进
1.5 版本之后的$.ajax
但是从v1.5开始,以上代码就可以这样写了:可以链式的执行
done或者
fail方法
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的原型。
记住一句话————虽然 JS 是异步执行的语言,但是人的思维是同步的————因此,开发者总是在寻求如何使用逻辑上看似同步的代码来完成 JS 的异步请求。而 jquery 的这一次更新,让开发者在一定程度上得到了这样的好处。
之前无论是什么操作,我都需要一股脑写到
callback中,现在不用了。现在成功了就写到
done中,失败了就写到
fail中,如果成功了有多个步骤的操作,那我就写很多个
done,然后链式连接起来就
OK 了。
和后来的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 deferred
上一节讲到 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 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 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函数),而不能去主动修改操作
一个“主动”一个“被动”,完全分开了。
promise 的概念
jquery v1.5 版本发布时间距离现在(2017年初春)已经老早之前了,那会儿大家网页标配都是 jquery 。无论里面的deferred和
promise这个概念和想法最早是哪位提出来的,但是最早展示给全世界开发者的是 jquery ,这算是
Promise这一概念最先的提出者。
其实本次课程主要是给大家分析 ES6 的
Promise
Generator和
async-await,但是为何要从 jquery 开始(大家现在用 jquery 越来越少)?就是要给大家展示一下这段历史的一些起点和发展的知识。有了这些基础,你再去接受最新的概念会非常容易,因为所有的东西都是从最初顺其自然发展进化而来的,我们要去用一个发展进化的眼光学习知识,而不是死记硬背。
相关文章推荐
- 深入理解 JavaScript 异步系列(2)—— jquery的解决方案
- 深入理解 JavaScript 异步系列(2)—— jquery的解决方案
- 深入理解 JavaScript 异步系列(5)—— async await
- 深入理解 JavaScript 异步系列(5)—— async await
- 深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
- 深入理解 JavaScript 异步系列(1)——基础
- 深入理解 JavaScript 异步系列(1)——基础
- 深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
- 深入理解 JavaScript 异步系列(1)——基础
- 深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
- 深入理解 JavaScript 异步系列(3)—— ES6 中的 Promise
- 深入理解 JavaScript 异步系列(1)——基础
- 深入理解 JavaScript 异步系列(5)—— async await
- 深入理解JavaScript系列
- 深入理解JavaScript系列(2) 揭秘命名函数表达式
- 深入理解JavaScript系列(8) S.O.L.I.D五大原则之里氏替换原则LSP
- 深入理解JavaScript系列(4) 立即调用的函数表达式