在常规的服务器端程序设计中, 比如说爬虫程序, 发送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. nginx : server_name localhost 和 chrome : Provisional headers are shown

    问题相关问题现象:解决思路解决方案总结 问题相关 nginx : server_name localhost chrome : Provisional headers are shown 问题现象: ...

  2. redis集群原理

    redis是单线程,但是一般的作为缓存使用的话,redis足够了,因为它的读写速度太快了.   官方的一个简单测试: 测试完成了50个并发执行100000个请求. 设置和获取的值是一个256字节字符串 ...

  3. 关于github中的README.md文件

    0x01 README.md文件是用Markdown语言编写的,md=Markdown; 在线编辑工具: https://stackedit.io/editor# https://maxiang.io ...

  4. GAME(A)性能测试过程模型

  5. stm32 复位后 引起引脚的变化,输出电平引起的问题

    在做项目的时候,需要通过蓝牙发送指令给STM32,使其复位,然后进入bootloader程序进行升级,但是复位后会导致蓝牙模块关机.stm32有个引脚连接着蓝牙的开关机引脚,高电平开机,低电平关机,我 ...

  6. HTML第二课

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  7. 2017-3-2 C#基础 集合

    要使用集合必须先引用命名空间,using System.Collections; 集合与数组的不同: 数组:同一类型,固定长度集合:不同类型,不固定长度 集合主要分为六大类:普通集合,泛型集合,哈希表 ...

  8. css模拟Bootstrap响应式布局——栅格

    做作业的时候遇见用css模拟Bootstrap的栅格布局,学习了一下.发现这个很有用,用来在不同的平台上得到很好地用户体验,比如Phone.Pad.大屏幕显示器.小屏幕显示器.自己模拟用css写了一下 ...

  9. Kubernetes运维生态-Heapster分析

    Heapster在Kubernetes的运维生态中如下:集群的容器的监控数据收敛汇聚层 heapster1.0版本后内部分为event和metric两个进程,可制作为两个docker镜像部署为两个独立 ...

  10. 重新认识JavaScript

    JavaScrpit在我眼中一直是web前端脚本语言,而这段时间的一些工作,让我对JavaScript有了一个全新的认识. 公司准备启动的一个手游项目,服务器端准备使用网易的开源框架pomelo.po ...