在常规的服务器端程序设计中, 比如说爬虫程序, 发送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. 模块化写法-IIFE解析

    一.IIFE解释 全拼Imdiately Invoked Function Expression,立即执行的函数表达式.  像如下的代码所示,就是一个匿名立即执行函数: (function(windo ...

  2. 快速排序的C语言实现

    #include <stdio.h> int qSort(int a[],int i,int j) { int h = i; int r = j; int x = a[h]; int f ...

  3. C# Redis学习系列一:Redis的认识、下载、安装、使用

    一.认识Redis 1. Redis 是一个高性能的key-value数据库. 2. 它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sor ...

  4. ASP.NET Core MVC之Serilog日志处理,你了解多少?

    前言 本节我们来看看ASP.NET Core MVC中比较常用的功能,对于导入和导出目前仍在探索中,项目需要自定义列合并,所以事先探索了如何在ASP.NET Core MVC进行导入.导出,更高级的内 ...

  5. 学习接水系统(java+thread线程)

    (一)项目框架分析 对于学生并发接水项目,根据面向对象的思想,需要创建两个对象,即学生和水龙头. 接下来主要讲解不排队接水和排队接水两张情况. 项目的目录文件如下: (二)不排队接水 假设有四个学生小 ...

  6. linux 下apache2 安装

    Apache安装要求 安装APR.APR-Util.PCRE,gcc-c++等包 Apache HTTP Server  http://httpd.apache.org/download.cgi#ap ...

  7. JDK 安装过程

    1.首先是下载jdk:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 2.下载完 ...

  8. 问题 : lang.NoClassDefFoundError: org/springframework/core/annotation/AnnotatedElementUtils,的解决方法

    今天在做junit 测试的时候  出现了一个问题,花了一段时间 才解决. java.lang.NoClassDefFoundError: org/springframework/core/annota ...

  9. 辗转相除法求H.C.F小结

    辗转相除法 大纲: 问题 原理 反思 1.     问题 一个试题,请完成以下填空 下列程序是利用辗转相除法求H.C.F(最大公约数) include <stdio.h> int main ...

  10. synchronized和lock比对

    前言:在上面的博客说了synchronized的一些用法,下面我们再来看看lock,这个出现频率也是非常高的一个. 1:获取Lock锁的几种方式 前面说了synchronized有锁对象和锁类对象,当 ...