深入理解 JavaScript 异步系列(2)—— jquery的解决方案

第一部分,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对象,这个对象就带有 donefail的方法,并且是等着请求返回之后再去调用

改进之后的好处

这是一个标志性的改造,不管这个概念是谁最先提出的,它在 jquery 中首先大量使用并让全球开发者都知道原来 ajax 请求还可以这样写。这为以后的 Promise标准制定提供了很大意义的参考,你可以以为这就是后面 Promise的原型。

记住一句话———— 虽然 JS 是异步执行的语言,但是人的思维是同步的————因此,开发者总是在寻求如何使用逻辑上看似同步的代码来完成 JS 的异步请求。而 jquery 的这一次更新,让开发者在一定程度上得到了这样的好处。

之前无论是什么操作,我都需要一股脑写到 callback中,现在不用了。现在成功了就写到 done中,失败了就写到 fail中,如果成功了有多个步骤的操作,那我就写很多个 done,然后链式连接起来就 OK 了。

和后来的 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 越来越少)?就是要给大家展示一下这段历史的一些起点和发展的知识。有了这些基础,你再去接受最新的概念会非常容易,因为所有的东西都是从最初顺其自然发展进化而来的,我们要去用一个发展进化的眼光学习知识,而不是死记硬背。

求打赏

如果你看完了,感觉还不错,欢迎给我打赏 ———— 以激励我更多输出优质内容

深入理解 JavaScript 异步系列(2)—— jquery的解决方案

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

Original: https://www.cnblogs.com/wangfupeng1988/p/6515779.html
Author: 王福朋
Title: 深入理解 JavaScript 异步系列(2)—— jquery的解决方案

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/536489/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • JavaScript进行WebSocket字节流通讯示例

    websocket进行通讯时,可以选择采用字符串或者字节流的传输模式。但在发送与接收时,需要考虑数据的分包,即分成一个个请求与响应消息。无论是采用哪种传输模式,都不免要遇到这个问题…

    JavaScript 2023年5月29日
    058
  • javascript错误处理与调试(转)

    JavaScript 在错误处理调试上一直是它的软肋,如果脚本出错,给出的提示经常也让人摸不着头脑。 ECMAScript 第 3 版为了解决这个问题引入了 try…c…

    JavaScript 2023年5月29日
    064
  • 深入理解 JavaScript 异步系列(1)——基础

    前言 2014年秋季写完了《深入理解javascript原型和闭包系列》,已经帮助过很多人走出了 js 原型、作用域、闭包的困惑,至今仍能经常受到好评的留言。 很早之前我就总结了J…

    JavaScript 2023年5月29日
    056
  • 使用Javascript来创建一个响应式的超酷360度全景图片查看幻灯效果

    360度的全景图片效果常常可以用到给客户做产品展示,今天这里我们推荐一个非常不错的来自Robert Pataki的360全景幻灯实现教程,这里教程中将使用javascript来打造…

    JavaScript 2023年5月29日
    049
  • JavaScript 中 call()、apply()、bind() 的用法

    "use strict"; var name = ‘小王’,age=10; var obj = { name:’小李’, age:20, getInfo(fro…

    JavaScript 2023年5月29日
    034
  • 通过JavaScript实现在线测试HTTP延迟方法

    我们在选购 VPS 时,有时候需要看一下网络质量如何,但是有的商家会禁止 ping,或者数据中心可能会很多,一 个个测试需要花费较多时间,因此考虑是否能够实现批量测试 HTTP 延…

    JavaScript 2023年5月29日
    043
  • JavaScript 多级联动浮动(下拉)菜单 (第二版)

    上一个版本(第一版请看这里)基本实现了多级联动和浮动菜单的功能,但效果不是太好,使用麻烦还有些bug,实用性不高。这次除了修改已发现的问题外,还对程序做了大幅调整和改进,使程序实用…

    JavaScript 2023年5月29日
    087
  • JavaScript 拖放效果

    拖放效果,也叫拖拽、拖动,学名Drag-and-drop ,是最常见的js特效之一。如果忽略很多细节,实现起来很简单,但往往细节才是难点所在。这个程序的原型是在做图片切割效果的时候…

    JavaScript 2023年5月29日
    062
  • 在浏览器中高效使用JavaScript module(模块)

    在浏览器中也可以使用JavaScript modules(模块功能)了。目前支持这一特性的浏览器包括: Safari 10.1. 谷歌浏览器(Canary 60) – 需要在 ch…

    JavaScript 2023年5月29日
    062
  • Node.js:用JavaScript写服务器端程序-介绍并写个MVC框架

    (注:1、本文基于Node.js V0.3.6; 2、本文假设你了解JavaScript; 3、本文假设你了解MVC框架;4、本文作者:QLeelulu,转载请注明出处。5、本文示…

    JavaScript 2023年5月29日
    069
  • JavaScript的Prototype实现

    JavaScript的Prototype实现 作者:Jeff.Yan(阎宏),BlueSwing.Liu(刘如鸿) 模式: Prototype(原始模型模式或者原型模式) 定义: …

    JavaScript 2023年5月29日
    073
  • JavaScript日期对象使用总结

    javascript Date日期对象的创建 创建一个日期对象: var objDate=new Date([arguments list]); 我总结了参数形式主要有以下3种: …

    JavaScript 2023年5月29日
    052
  • JavaScript的this和作用域

    本文主要讨论一下JS的作用域和 this关键字。作用域,就是你的方法或者变量可访问的区域,是他们执行的上下文。如果你见过这样的代码: function someFunc() { v…

    JavaScript 2023年5月29日
    058
  • JavaScript Table排序

    序二(09/05/03) 近来还是那么忙,趁五一更新一下程序吧。这个版本主要增加和改进了以下东西:1,对字符串改用localeCompare来比较;2,一次排序中能使用多个排序对象…

    JavaScript 2023年5月29日
    065
  • Javascript :this关键字 详解

    由于 Javascript(简称:JS)中的 this 理解起来较为复杂,使得很多入门新手和一些进入前端圈子有些年限的工程师们都对其的用法有些模糊。所以今天为大家带来 Javasc…

    JavaScript 2023年5月29日
    054
  • javascript中判断数据类型

    编写javascript代码的时候常常要判断变量,字面量的类型,可以用typeof,instanceof,Array.isArray(),等方法,究竟哪一种最方便,最实用,最省心呢…

    JavaScript 2023年5月29日
    050
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球