在常规的服务器端程序设计中, 比如说爬虫程序, 发送http请求的过程会使整个执行过程阻塞,直到http请求响应完成代码才会继续执行, 以php为例子

$url = "http://www.google.com.hk";
$result = file_get_contents($url);
echo $result;

当代码执行到第二行时,程序便陷入了等待,直到请求完成,程序才会继续往下跑将抓取到的html输出。这种做法的好处是代码简洁明了,运行流程清晰, 容易维护。 缺点就是程序的运行速度依赖于http请求的响应时间,影响程序的运行效率。 然而, 因为web程序本身特质的原因,这种问题是避无可避的,程序依赖于http响应的结果和保证自身的迅速响应两者之间是存在矛盾的, 肯定无法兼顾。

但是在客户端程序或者非http应用的场景下是不存在类似的冲突的, 在Java或C#客户端编程中,碰到这种问题一般都是开启两个线程各干各的。而在JavaScript中,因为语言本身不支持多线程, 所以此类问题是使用回调函数来解决。

以最简单的前端ajax请求为例

 $.get("data.json", function ( response ) {
console.log("2");
});
console.log("1")

代码先输出1,再输出2,整个程序执行流程并未因http请求而被阻塞,回调函数方案完美的把问题解决。 然而,这只是最简单回调函数示例,假如回调函数嵌套了许多层呢?

$.get("data1.json", function (response) {
$.get(response.url, function (response) {
$.get(response.url, function (response) {
console.log(response);
});
});
});

回调嵌套的越深,代码运行逻辑就越难理清楚, 如果在上面代码的基础上再混入一些复杂的业务逻辑,那代码将会极难维护, 到时候遇到问题了剪不断理还乱的感觉肯定会让人红着眼睛骂娘。 虽然这种回调嵌套的场景在web前端开发中比较罕见, 但在nodejs服务器端开发领域还是常见的。 那如何克服这个问题?假如用php来写, 那便是一件很轻松的事了。

$response = file_get_contents("data1.json");
$response1 = file_get_contents($response["url"]);
$response2 = file_get_contents($response1["url"]);
echo $response;

以php发送http请求的方案来实现, 代码逻辑就清晰了许多。 在古时候 ,JavaScript想以这种方式实现ajax那就是痴人说梦,但是当JavaScript升级至es6版本后,通过特定的途径也可实现这种写法。 在网上这种写法被称之为“以同步的方式编写异步代码”,但是我觉得这种说法容易把人给搞迷糊,可以直接把这种写法称之为:“同步写法”, 因为里面的异步执行已经被隐藏了起来。 要实现这种写法必须使用async和await这两个关键字。在两个关键字是es7的范畴, es6还不支持,但是可以通过特定的工具将使用这两个关键字的代码转为es6的代码去执行, 比如说TypeScript和babel, 在此文中使用的代码示例都是由TypeScript实现。对于async和await的底层机制这里就不详述了, 以免将文章的篇幅拖的很长,这里就讲解一下这两个关键字能实现的效果。 先把上面用JavaScript实现的多层嵌套回调用同步的方式来改写, 代码如下

async function ajax(url) {
return new Promise(function (resolve, reject) {
let ajaxSetting = {
url: url,
success: function (response) {
resolve(response);
},
error: function () {
reject("请求失败");
}
}
$.ajax(ajaxSetting); });
} async function run() {
let response1 = await ajax("data1.json");
let response2 = await ajax(response1["url"]);
let response3 = await ajax(response2["url"]);
console.log(response3);
} //不阻塞
run();

代码由ajax和run这两个函数组成, ajax是对jquery ajax的封装,使之能不使用回调函数就能获得ajax的响应结果。 当函数被声明为async类型时,如果这个函数要有返回值 ,并且返回值要在某个回调函数中获得,那么这个函数的返回结果就只能是一个 Promise对象,就像示例的ajax函数一样,返回值如果是其它类型那就达不到期望的效果。 Promise构造函数的参数是一个函数,resolve和reject分别是这个函数的两个参数,同时这两个参数自身也是函数类型,这两个参数有着重要的意义,在这里它们的作用就是将ajax的响应内容给返回出去,resolve表示返回正常状况下的值, reject表示返回异常状态下的值。按照传统的编码方式, 可以将reject看作是抛出了一个异常,像throw "请求失败", 这样,在函数调用的外部可以用try catch进行捕获。将值传出去为什么要通过这两个参数呢?因为没辙啊, 试想一下,ajax的回调函数中使用return语句, 意义何在?因此也只能变向的通过Promise将返回值扔给外部的调用者。 所以,使用async和await的第一个要点就是

当函数要获得异步结果时,可以函数声明为async类型, 函数的返回值设为Promise类型对象,而Promise中的resolve和reject是用来向async函数返回结果的, 功效如同普通函数的return语句。

async类型函数要怎么使用呢?有两种方法,一种是直接调用, 直接调用的话函数前面async关键字就被忽略了, 调用函数返回的结果就是一个Promise对象, Promise对像如何使用在这里不进行深究,大致就是像下面这样的写法

ajax("data1.json").then(function( response ){
……
});

还是以回调函数的形式出现,改进代码所带来的意义并没有体现。

另一种方法是在调用函数时加上await关键字,await的意义就在于接收async函数中的Promise对象中resolve和reject传递的值 ,而且除非resolve和reject这两个函数在回调函数中被调用到了, 否则代码就一直被阻塞在那里?换句话说, resolve和reject的调用是用来通知await等待结束,代码可以继续执行了。 这种写法不就是之前想方设法想实现的同步写法么?跟php的写法区别在于多了 await、async、Promise这三个概念, 但是在不考虑其中的内部运行原理的话, 代码的执行流程上已经和同步的写法没一丝区别了。有一点需要注意, 假如需要在函数中使用await调用,那么这个函数也必须被声明为async类型, 否则编译出错, 程序无法正常运行。 所以, 第二个要点就是

await就是用来等待Promise对象中resolve和reject这两个函数的执行的,并且将这两个函数传递的参数当作返回结果赋给变量,如同run函数中的代码示例那样。 别外, await必须被夹在两个async中间, 一个是await调用的函数,一个是await所在的函数。

至于Promise中的reject,就是用来抛异常的, 在外await调用之外可使用try catch捕获,代码如下

async function run() {
try {
let response1 = await ajax("data1.json");
let response2 = await ajax(response1["url"]);
let response3 = await ajax(response2["url"]);
console.log(response3);
} catch (ex) {
console.log(ex);
}
}

此文只是纯粹的讲解 await和async能起什么样的作用?如何使用?至于深入细节方面的知识, 有兴趣的同学可以去阮一峰的博客里学习, 附上链接地址

http://www.ruanyifeng.com/blog/2015/05/async.html

不使用回调函数的ajax请求实现(async和await简化回调函数嵌套)的更多相关文章

  1. 关于iframe和div窗口中ajax请求200状态时执行的回调问题

    上一篇说了在ajax回调里面处理iframe窗口的刷新问题,这一篇记录一下遇到的一个分别在iframe和div窗口中ajax请求200状态时执行的回调问题. 我们先来看一下ajax请求的写法(这里使用 ...

  2. ajax请求成功但不执行success-function回调函数的问题

    在success:function(data){}下面加个error:function(){},看看是不是出错了走了error.如果是,说明返回值类型不符合要求. 比如:下面代码返回String类型. ...

  3. 关于ajax请求status 200 却进入error 回调函数或显示跨域问题的解决方案及原因

    这虽然不是前端的问题吧,但如果遇到那种不靠谱的后台 还是可以拿来打脸的 转:https://segmentfault.com/a/1190000012469713

  4. Ajax请求成功,进入error回掉函数

    后台无返回值,则不需要datatype.

  5. 采用指数退避算法实现ajax请求的重发,全部完成时触发回调函数

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  6. ajax请求的封装

    前端的工作,免不了要用到交互,请求后端的数据,可能大多人一直选择用jq封装好的方法直接使用,要知道封装这个事我们自己也可以的,今天给大家介绍一种封装方法,而且连跨域问题都不在话下,有了这个函数,是不是 ...

  7. ajax请求的异步嵌套问题分析

    (本文章以as3代码为例) 问题的产生 在前端开发时,经常会使用到Ajax(Asynchronous Javascript And XML)请求向服务器查询信息(get)或交换数据(post),aja ...

  8. Jquery监听AJAX请求

    .ajaxComplete() 当Ajax请求完成后注册一个回调函数.这是一个 AjaxEvent. .ajaxError() Ajax请求出错时注册一个回调处理函数,这是一个 Ajax Event. ...

  9. 利用JQUERY实现多个AJAX请求等待

    利用JQUERY实现多个AJAX请求等待 li {list-style-type:decimal;}.wiz-editor-body ol.wiz-list-level2 > li {list- ...

随机推荐

  1. 全球分布式数据库:Google Spanner(论文翻译)

    本文由厦门大学计算机系教师林子雨翻译,翻译质量很高,本人只对极少数翻译得不太恰当的地方进行了修改. [摘要]:Spanner 是谷歌公司研发的.可扩展的.多版本.全球分布式.同步复制数据库.它是第一个 ...

  2. Centos 7安装oracle 11g R2问题及解决方法汇总

    自己新博客的链接:http://www.pythonsite.com/2017/02/14/centos-7%E5%AE%89%E8%A3%85oracle-11g-r2%E9%97%AE%E9%A2 ...

  3. github在windows下的安装和基本使用

    1.在win下安装github时花费的时间是非常长的,有时还会出现因各种原因安装不成功.离线包本地安装方便的解决了此问题.点击http://pan.baidu.com/s/1boGrNLP可下载,解压 ...

  4. C语言 extern4 全局数组

    headB.h中: #ifndef headB_H #define headB_H ; ]={,,,,}; #endif .c文件中: #include "headB.h" #in ...

  5. mongodb终端指令

    -h [--help]显示此使用信息   --version显示版本信息   -f [--config] arg配置文件指定                                       ...

  6. ajax提交File文件

    <script type="text/javascript">    $(function() {        $("input[name='image'] ...

  7. C# 数据类型 数据转换 自己的见解和方式

    数据类型分为:基本数据类和引用类型, 基本数据类型又分为整型,浮点型,字符型,布尔型. 引用类型又分为:字符串型,日期时间,枚举类型,结构类型. int long float = 10.5f; dou ...

  8. Smarty3配置及入门语法

    一.Smarty3配置 下载Smarty文件 在Smarty的官方网站下载Smarty文件,解压下载到的Smarty文件,Smarty的库文件就在libs文件夹中. 我使用的PHP调试环境的程序集成包 ...

  9. 四色GDOI&GDOI2015滚粗记

    好吧自己太弱写不了什么四色NOI只能学学别人写个四色GDOI了...首先自己还是太弱所以就被学校卡了个名额就进不了省队了QAQ.自己GDOI觉得考得不错可是NOIP毕竟少了人家5分根本追不上去好不QA ...

  10. 【排序算法】直接选择排序算法 Java实现

    基本思想 直接选择排序是从无序区选一个最小的元素直接放到有序区的最后. 初始状态:无序区为a[1...n],有序区为空. 第一次排序:在无序区a[1...n]中选出最小的记录a[k],将它与有序区的第 ...