ajax 请求 回调,Ajax请求回调地狱及解决方案(promise、async和await)

谈及回调地狱发生得情况和解决办法,就必须追溯到原生ajax请求。

先列出服务器提供的数据接口:

// 服务器端接口

app.get('/data1', (req, res) => {

res.send('hi')

})

app.get('/data2', (req, res) => {

res.send('hello')

})

app.get('/data3', (req, res) => {

res.send('nihao')

})

// 启动监听

app.listen(3000, () => {

console.log('running...')

})

原生ajax请求步骤

var xhr = new XMLHttpRequest();

xhr.open('get','http://localhost:3000/data1');

xhr.send(null);

xhr.onreadystatechange = function() {

if(xhr.readyState == 4 && xhr.status == 200) {

// 获取后台数据

var ret = xhr.responseText;

console.log(ret)

}

}

又因为发送请求的以上代码需要经过反复复用,我们就需要将它封装为函数,以减少代码的冗余度。下面请看两个封装方法(都是错误的封装):

错误封装1:发生错误的原因是queryData函数本身没有返回值,会默认返回undefined,return ret是写在queryData的内层函数中的。

function queryData(path) {

var xhr = new XMLHttpRequest();

xhr.open('get','http://localhost:3000/'+path);

xhr.send(null);

xhr.onreadystatechange = function() {

if(xhr.readyState == 4 && xhr.status == 200) {

// 获取后台数据

let ret = xhr.responseText;

return ret;

}

}

}

let res = queryData(‘data1’);

console.log(res);  // 结果为:undefined

这样很容易就让我们想到另一种封装方法——把ret在外层函数中返回。然而这就产生了另一种错误的封装效果。

错误封装2:这种情况下发生错误的原因是ajax请求时异步的,数据ret还没有修改成功时,就已经执行了queryData函数的return代码。这时ret的值并没有被修改,当然还是null。

function queryData(path) {

var xhr = new XMLHttpRequest()

xhr.open('get', 'http://localhost:3000/'+path)

xhr.send(null)

var ret = null

xhr.onreadystatechange = function() {

if (xhr.readyState == 4 && xhr.status == 200) {

// 获取后台数据

ret = xhr.responseText

}

}

return ret

}

let res = queryData('data1')

console.log(res)  // 结果为:undefined

要想执行异步操作代码的返回内容,就需要使用回调函数,下面介绍一种正确的封装方法:

function queryData(path, callback) {

var xhr = new XMLHttpRequest();

xhr.open('get','http://localhost:3000/' + path);

xhr.send(null);

xhr.onreadystatechange = function() {

if(xhr.readyState == 4 && xhr.status == 200) {

// 获取后台数据

var ret = xhr.responseText;

callback(ret);

}

}

}

queryData('data1',function(ret) {

console.log(ret) // 结果为:hi

})

但是,如果想要按顺序获取接口'data1'、'data2'、'data3'中的数据,就会进行下面的操作,也就造成了回调地狱的问题。

queryData('data1', function(ret) {

console.log(ret)   // 按顺序第一个输出为:hi

queryData('data2', function(ret) {

console.log(ret)  //按顺序第二个输出为:hello

queryData('data3', function(ret) {

console.log(ret)  // 按顺序第三个输出为:nihao

});

});

});

promise方式

为了改造上面的回调地狱问题,诞生了promise。promise其实就是一种语法糖(代码形式发生改变、但是功能不变)。

function queryData(path) {

return new Promise(function(resolve, reject) {

// 需要在这里处理异步任务

var xhr = new XMLHttpRequest();

xhr.open('get','http://localhost:3000/' + path);

xhr.send(null);

xhr.onreadystatechange = function() {

// 该函数何时触发?xhr.readyState状态发生变化时

if(xhr.readyState != 4) return;

if(xhr.readyState == 4 && xhr.status == 200) {

// 获取后台数据

var ret = xhr.responseText;

// 成功的情况

resolve(ret);

} else {

// 失败的情况

reject('服务器错误');

}

}

})

}

queryData('data1')

.then(ret=>{

console.log(ret) // 按顺序第一个输出为:hi

// 这里返回的是Promise实例对象,下一个then由该对象调用

return queryData('data2');

})

.then(ret=>{

console.log(ret);  // 按顺序第二个输出为:hello

return queryData('data3');

})

.then(ret=>{

console.log(ret)  // 按顺序第三个输出为:nihao

})

对于上面代码中使用.then调用的情况,有几点说明:

queryData('data1')

.then(ret=>{

console.log(ret)  // 顺序输出第一个结果为:hi

// 如果在then方法中没有返回Promise实例对象,那么下一个then由默认产生的Promise实例对象调用

})

.then(ret=>{

console.log('-------------------' + ret)  // 顺序输出第二个结果为:----------------------undefined

// 如果在then中显式地返回一个具体数据,那么下一个then可以获取该数据

return 456;

})

.then(ret=>{

console.log('-------------------' + ret)  // 顺序输出第三个结果为:----------------------456

})

上面的代码第二个.then中return的是456,为什么能继续调用.then方法呢?

这是因为return 456 实际上可以理解为下面的三种表示方式:

// Promise.resolve的作用:就是把数据转化为Promise实例对象

// 方式一:

return Promise.resolve(456);

// 方式二:

return new Promise(function(resolve, reject) {

resolve(99999);

})

// 方式三:

Promse.resolve = function(param) {

return new Promise(function(resolve, reject) {

resolve(param);

})

}

return Promise.resolve(88888);

promise对象除了.then方法外还有两个方法可以通过 . 调用,其中.finally是ES7中新增的方法。

.catch(ret=>{

// 发生错误时触发

console.log('error')

})

.finally(ret=>{

// 无论结果成功还是失败都触发:一般用于释放一些资源

console.log('finally')

})

虽然使用了promise对象,但是一路通过 . 调用方法进行下去,代码的可读性较差。

async和await

下面我们就提出解决回调地狱最好的一种方法,通过使用 async和 await。

function queryData(path) {

return new Promise(function(resolve, reject) {

// 需要在这里处理异步任务

var xhr = new XMLHttpRequest();

xhr.open('get','http://localhost:3000/' + path);

xhr.send(null);

xhr.onreadystatechange = function() {

// 当readyState值不为0的时候直接返回

if(xhr.readyState != 4) return;

if(xhr.readyState == 4 && xhr.status == 200) {

// 获取后台数据

var ret = xhr.responseText;

// 成功的情况

resolve(ret);

} else {

// 失败的情况

reject('服务器错误');

}

}

})

}

async function getAllData() {

// await执行流程是顺序执行

let ret1 = await queryData('data1');

let ret2 = await queryData('data2');

let ret3 = await queryData('data3');

console.log(ret1)

console.log(ret2)

console.log(ret3)

}

getAllData();

另外,有一点需要提起注意:async函数的返回值是Promise实例对象

async function getAllData() {

// await执行流程是顺序执行

let ret1 = await queryData('data1');

return 'hello';

}

var ret = getAllData();

console.log(ret)  // 这里输出一个promise对象,并且resolve的数据为hello

ret.then(res=>{

console.log(res)  // 这里输出结果为:hello

})

jQuery笔记之工具方法—Ajax 优化回调地狱

在上一篇文我们说到了回调地狱不好的地方,今天我们看看怎么来优化它,让它可以运用到实际开发中. 什么是回调地狱?回调地狱就是一个函数里面嵌套了所有功能函数,然后缩略图形成一个三角形. 这样的代码可复用性 ...

Ajax请求回调函数没有被调用

$.ajax({        type:"post",        url:"http://172.16.41.91:8080/FcsServletSSM/users ...

dotnet webservice处理数据量过大,ajax请求返回500错误解决方案

ajax请求webservice返回json数据,数据规模过大时ajax请求会得到500的响应,webservice+ajax处理大规模的数据需要在web.config中进行如下配置:

回调地狱以及用promise怎么解决回调地狱

哈哈哈,我又又又回来了,不好意思,最近枸杞喝的比较到位,精力比较旺盛. 现在我们来聊一聊啥是回调地狱,注意是回调地狱啊   不是RB人民最爱拍的那啥地狱啊,来吧,上车吧少年,这是去幼儿园的车 都让开, ...

promise, async和await

最开始实现异步的方法:回调函数 method1(function(err, result) { if (err) { throw err; } method2(function(err, result ...

ES系列之Promise async 和 await

概述 promise是异步编程的一种解决方案,比传统的解决方案—回调函数和事件—更合理更强大. 所谓的promise就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作的结果). Pro ...

[Winform]线程间操作无效,从不是创建控件的线程访问它的几个解决方案,async和await?

目录 概述 取消跨线程检查 使用委托异步调用 sync和await 总结 概述 最近在qq群里有一朋友,问起在winform中怎么通过开启线程的方式去处理耗时的操作,比如,查看某个目录下所有的文件,或 ...

$.when()方法监控ajax请求获取到的数据与普通ajax请求回调获取到的数据的不同

1.$.when(ajax).done(function(data)}); 2.$.ajax().done(function(data){}); 1中的data被封装进一个对象[data, &quot ...

jquery ajax请求回调

...

Zend Framework 入门(1)—快速上手

1. 安装 从 Zend Framework 的网页上下载最新版本.解压后,把整个目录拷贝到一个理想的地方,比如:/php/library/Zend. 打开 php.ini 文件,确认包含 Zend ...

C++ 我想这样用(四)

嗯,已经是第四篇了,这篇起我就要开始细说语法了,不过在那之前再次申明下主旨:"C++我想这样用" 系列文案是为C程序员打造的,不是C++程序员. 我的终极目标是:让那些觊觎面向对象 ...

在Windows下用gSoap实现简单加法实例

实现一个简单的a+b程序,在服务器端写一个程序,里面包含了a+b的函数,然后通过客户端代码向其发送两个数字,在服务器运算得到结果返回给客户端显示出来. 1.在gSoap的官网上下载文件夹,本人的版本是 ...

让CEF支持FLASH(非安装插件的形式)

测试环境: CEF3 + CefGlue 下载FLASH的NPAPI DLL文件 , 在CEF目录下新建文件夹plugins,然后把DLL文件放进去即可. 据说下面是PPAPI的方式,未测试 启动的时 ...

Hibernate利用纯sql

String hql = "select * from shop where shop.strid in(select strid from moneythreeshop where mon ...

java开发工具之myeclipse调优

-vmargs -Xms512m //堆的最小值-Xmx512m //堆的最大值(两者设置相同,避免运行时的自动扩张)-XX:PermSize=256m //永久代的最小值 -XX:MaxPermSi ...

WebGL编程指南案例解析之绘制三角形

//案例3.绘制三角形,将顶点数据存到缓冲区对象(gl.ARRAY_BUFFER)中,然后顶点着色器从里面读数据(3个顶点) //顶点着色器中去掉gl_PointSize = 10.0,绘制三角不能设 ...

通过acdbblockreference 获得块名

AcDbBlockReference *pBlkRef = AcDbBlockReference::cast(ent.object());     AcDbObjectId pBlkTblRecId; ...

HD-ACM算法专攻系列(14)——find your present (2)

问题描述: 源码: #include"iostream" #include"algorithm" using namespace std; bool cmp(i ...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值