不使用回调函数的ajax请求实现(async和await简化回调函数嵌套)

在常规的服务器端程序设计中, 比如说爬虫程序, 发送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

本文首发于微信公众号 「带你撸出一手好代码」,欢迎关注获取更多原创分享。

时间: 2024-10-03 23:10:08

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

通过在jquery中添加函数发送ajax请求来加载数据库数据,以json的格式发送到页面

通过在jquery中添加函数发送ajax请求来加载数据库数据,以json的格式发送到页面 从数据库中查询仓库信息,显示在下拉菜单中: 首先,引入js插件,这里使用jquery-1.8.3.js <script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.8.3.js"></script> 当页面加载完成后,就应该发送ajax请求到数据库,

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

上一篇说了在ajax回调里面处理iframe窗口的刷新问题,这一篇记录一下遇到的一个分别在iframe和div窗口中ajax请求200状态时执行的回调问题. 我们先来看一下ajax请求的写法(这里使用了jQuery) function check_pass(uid) { $.ajax({ type: "GET", url: siteurl, dataType: "json", data:{ "c":"api", "m

Jquery监听AJAX请求

.ajaxComplete() 当Ajax请求完成后注册一个回调函数.这是一个 AjaxEvent. .ajaxError() Ajax请求出错时注册一个回调处理函数,这是一个 Ajax Event. .ajaxSend() 在Ajax请求发送之前绑定一个要执行的函数,这是一个 Ajax Event. .ajaxStart() 在AJAX 请求刚开始时执行一个处理函数. 这是一个 Ajax Event. .ajaxStop() 在AJAX 请求完成时执行一个处理函数. 这是一个 Ajax Eve

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

(本文章以as3代码为例) 问题的产生 在前端开发时,经常会使用到Ajax(Asynchronous Javascript And XML)请求向服务器查询信息(get)或交换数据(post),ajax请求都是异步响应的,每次请求都不能同步返回结果,而且多次请求嵌套在一起时,逻辑很难处理,怎么办呢? 在as3中,get请求的写法通常如下 public static function httpGet(url:String):void { var httpService:HTTPService =n

防止重复发送Ajax请求的解决方案

防止重复发送Ajax请求的解决方案 这篇文章主要介绍了防止重复发送Ajax请求的解决方案,感兴趣的小伙伴们可以参考一下 在页面中有多个按钮,点击该按钮可以异步的去服务端读取数据,然后在前端将数据展示出来. 每个按钮点击请求的页面都是同一个,但是请求的参数不同,所以返回的内容就不同. 在连续点击多个按钮的时候就会发出多个异步请求.那么根据请求返回的快慢(因为不同按钮参数不同,返回内容不同,所以会有快慢之分),数据会依次的展示出来,那么就会出现一个先点击的按钮,由于他请求的数据量比较大,导致数据被后

封装的ajax请求

在做登录注册这类提交表单数据时,我们经常需要局部刷新网页来验证用户输入的信息,这就需要用到ajax请求,我们通常需要获取表单中的数据,发起ajax请求,通过服务程序,与数据库的数据进行比对,判断信息的正确与否.这儿也将ajax请求进行了封装,当我们在项目中需要多次ajax请求时,就可以用封装的函数了,不用每次都写ajax请求,提高了代码的利用率和工作效率.当然封装得不一定很完美,网上也有很多优秀的封装好的ajax请求.也是通过闭包思想,具体的封装思路,在代码注释中也写得很详细了 这是封装的aja

ajax请求获取的数据无法赋值给全局变量问题总结

一.总结: 1.问题描述: 今天做项目遇到在用表单显示详细信息的过程中ajax请求获取的数据无法赋值给全局变量的情况,从列表页面进入详情页,在详情页面被渲染了之后就会调用js文件里的接口向服务器请求数据,除了详情页面被加载之后需要向服务器发送ajax请求,在详情页面还有几个表单控件的属性需要去请求服务器获取实际项目中要求要显示的数据. 如下代码,直接在ajax请求中改变表单控件的值,避开了给全局变量赋值. 给全局变量赋值的解决办法,给ajax请求设置async为false,表示请求为同步请求:

关于ExtJS的两种Ajax请求和回调问题(转载)

1. Ext.form.Action.Submit的配置选项success.failure 根据返回json中success属性判断的,如果success为true,则success,false则failure,如果无json中无success属性,failure,故要提示操作是否成功,必须要返回success属性. loginFormPanel.getForm().submit({ method:'POST', params:{ username:username, password:pass

Ajax请求状态200,却走error的函数

已经将近一个月没有正式使用Ajax的请求代码了,参加工作后,这是第一次使用,感觉有些生疏,但基本代码还是记得的,写起来也不费劲,但正因为如此,问题就来了,由于经验不足,本来导致问题的原因不是那个,我却误以为是那个,于是调试花费了很久. Ajax的部分代码如下: $.ajax({                url:'http://XXXXX/practice/pj2/Merchant.class.php',                type: "POST",