您的位置:首页 > Web前端 > JQuery

深入理解 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 越来越少)?就是要给大家展示一下这段历史的一些起点和发展的知识。有了这些基础,你再去接受最新的概念会非常容易,因为所有的东西都是从最初顺其自然发展进化而来的,我们要去用一个发展进化的眼光学习知识,而不是死记硬背。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: