异步ajax的同步调用解决方法(js小白的理解)

前言
最近,因为需要在前端使用ajax调用后端接口,但是由于ajax是异步方式工作的,所以遇到了一些问题。我本身是一个Java程序员,所以使用Java的同步思维来处理这些问题,就遇到了麻烦。(PS:虽然Java也有异步编程,但是毕竟平时还是使用的同步方法来编程的。)

举一个例子:
三个ajax函数:fun1()、fun2()、fun3()
假设它们是互相依赖的,必须是顺序执行的,即fun1、fun2、fun3。

作为一个java程序员,我立刻就写出来了:

fun1();
fun2();
fun3();

然后,执行以后才发现似乎有问题。它们执行的顺序是随机的(取决于各自执行需要的时间),所以这就很尴尬了 ,所以去查了解决方案,这里特来记录一下。我这里只是提到解决方法,但是不会去详细说原因,至于为什么,因为我也只是一个前端小白,我是为了解决问题的,而不是为了学习的,所以不必深入了解这方面的知识。

需求模拟

这里我们来模拟这个需求,我的思路是:
模拟多个ajax异步调用,然后直接异步调用它们和对它们的异步调用进行同步处理

需求: 这里以一篇古文《凤求凰》为例:凤求凰内容、凤求凰配图、凤求凰作者介绍,这三个使用三个ajax函数来加载。由于ajax是异步的,所以我这里无法保证它们三个出现的顺序。

目标: 保证加载顺序是:凤求凰内容、凤求凰配图、凤求凰作者介绍。但是,异步调用取决于这个方法的执行时间,所以这里我强制给每个方法设置一个延时执行的时间,然后本地调用去除网络环境干扰,让它们的执行完毕的顺序是固定的,这样方便观察结果。

实验环境搭建

这里搭建一个小的实验,来模拟上面提出的需求。
准备我们的实验环境:
因为我不了解js,这里就直接借用了菜鸟教程的方法:Node.js Express 框架
这里选择node.js的原因是它搭建一个http服务器很快,本来想用java写一个简单的出来,但是我发现node.js的实在是太好用了,这样也好,多了解其它语言,思维才能更加开阔!

实验demo目录结构

目录结构

node.js搭建的简单http服务器代码

var express = require("express");
var app = express();

app.use("/", express.static("resource"));

app.get("/poem", function(req, res) {
	let poem = `有一美人兮,见之不忘。
	一日不见兮,思之如狂。
	凤飞翱翔兮,四海求凰。
	无奈佳人兮,不在东墙。
	将琴代语兮,聊写衷肠。
	何时见许兮,慰我彷徨。
	愿言配德兮,携手相将。
	不得於飞兮,使我沦亡。`;
	res.send(poem);
});

app.get("/author", (req, res) => {
	let author = `司马相如(约前179年—前118年),字长卿,汉族,蜀郡成都人,
	祖籍左冯翊夏阳(今陕西韩城南)侨居蓬州(今四川蓬安)西汉辞赋家,中国文
	化史文学史上杰出的代表。有明显的道家思想与神仙色彩。作品词藻富丽,结构宏大,
	使他成为汉赋的代表作家,后人称之为赋圣和“辞宗”。他与卓文君的爱情故事也广为流
	传。鲁迅的《汉文学史纲要》中还把二人放在一个专节里加以评述,指出:“武帝
	时文人,赋莫若司马相如,文莫若司马迁。`;
	res.send(author);
});

app.use("/picture", express.static("resource"));

var server = app.listen(9000, () => {
	var host = server.address().address;
	var port = server.address().port;
	console.log("http://%s:%s",host, port);
});

4个路由函数
第一个是访问根路径下面的index.html文件。
后面三个就是简单的api,第二个和第三个是返回一个字符串,第三个路由是返回路径下面的图片。

index.html

<!DOTYPE html>
<html>
	<head>
		<title>Promise</title>
		<meta charset="utf-8"/>
		<style>
			#main {
				margin: 0 auto;
				width:860px;
			}
			#sync {
			margin-right:30px;
				width: 400px;
				float: left;
			}
			
			#async {
			margin-left:30px;
				width: 400px;
				float: left;
			}
		</style>
		<script>
			function getDataFirst(id) {
				let xhr = new XMLHttpRequest();
				xhr.onload = function() {
					let p = document.createElement("p");
					p.innerHTML = xhr.responseText;
					p.setAttribute("id", "p1");
					document.getElementById(id).appendChild(p);
				}
				
				xhr.onerror = function() {
					document.getElementById(id).innerHTML = "The request occurs an Error!";
				}
				
				xhr.open("GET", "/poem");
				xhr.send();
			}
			
			function getDataSecond(id) {
				let xhr = new XMLHttpRequest();
				xhr.onload = function() {
					let blob = new Blob([xhr.response], {type: "image/jpg"});
					let reader = new FileReader();
					reader.readAsDataURL(blob);
					reader.onload = function(e) {
					  let img = document.createElement("img");
					  img.src = e.target.result;
					  img.setAttribute("id", "img");
					  img.setAttribute("width", "300px");
					  document.getElementById(id).appendChild(img);
					}
				}
				
				xhr.onerror = function() {
					document.getElementById(id).innerHTML = "The request occurs an Error!";
				}
				
				xhr.open("GET", "/picture/img.jpg");
				xhr.responseType = "blob";
				xhr.send();
			}
			
			function getDataThird(id) {
				let xhr = new XMLHttpRequest();
				xhr.onload = function() {
					let p = document.createElement("p");
					p.innerHTML = xhr.responseText;
					p.setAttribute("id", "p2");
					document.getElementById(id).appendChild(p);
				}
				
				xhr.onerror = function() {
					document.getElementById(id).innerHTML = "The request occurs an Error!";
				}
				
				xhr.open("GET", "/author");
				xhr.send();
			}
			
			function getData(id) {
			
				setTimeout(() => {
					(id => getDataFirst(id))("left")
				}, 2500);
				setTimeout(() => {
					(id => getDataSecond(id))("left")
				}, 1500);
				setTimeout(() => {
					(id => getDataThird(id))("left")
				}, 1000);
			}
			
			function func(func, id, delay) {
				return new Promise((resolve, reject) => {
					setTimeout(() => {
						func(id); 
						resolve();  // 这里设置它一定成功
					}, delay);
				});
			}
			
			async function asyncGetData(id) {
				await func(getDataFirst, id, 2500);
				await func(getDataSecond, id, 1500);
				await func(getDataThird, id, 1000);
			}
		</script>
	</head>
	
	<body>
		<div id="main">
			<div id="sync">
				<button onclick="getData('left')">非同步异步加载</button>
				<div id="left"></div>
			</div>
		
			<div id="async">
				<button onclick="asyncGetData('right')">同步异步加载</button>
				<div id="right"></div>
			</div>
		</div>
	</body>
</html>

直接调用的方式:
这里设置三个ajax函数的调用时间分别为:2500ms、1500ms、1000ms。
这样的话,它们三个在2500ms内会全部执行完毕,并且完成顺序是:
getDataThird
getDataSecond
getDataFirst

运行结果是:先出现凤求凰的作者介绍、凤求凰的配图、凤求凰内容。
PS:图片在下方呢,一定要坚持看下去呀!

function getData(id) {
	setTimeout(() => {
		(id => getDataFirst(id))("left")
	}, 2500);
	setTimeout(() => {
		(id => getDataSecond(id))("left")
	}, 1500);
	setTimeout(() => {
		(id => getDataThird(id))("left")
	}, 1000);
}

对方法进行同步:
上面那种方式显然不是我们想要的,按照人的习惯顺序应该是:凤求凰内容、凤求凰配图、凤求凰作者介绍。

所以这里使用一个技术:Promise(见文章末尾说明部分
首先定义一个函数,其返回值为一个Promise,第一个参数是我们需要执行的函数,第二个参数是元素的id(它只是设置在右边显示而已,不重要)以及第三个参数是延迟执行的时间(也不重要),然后设置该函数一定能成功,即最后执行resolve()。

然后就可以同步调用了:getData2() 函数。

function func(func, id, delay) {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			func(id); 
			resolve();  // 这里设置它一定成功
		}, delay);
	});
}

function getData2(id) {
	func(getDataFirst, id, 2500)
	.then(func(getDataSecond, id, 1500))
	.then(func(getDataThird, id, 1000));
}

ES6的新方法:
上面这样写法,特别是那个then,另外还有catch和finally,但是这里没有体现,这样调用会显得特别麻烦,所以ES6引入了两个关键字:async和wait。
async和wait用于简化Promise的编程,所以上面的getData2可以改写为如下:

async function asyncGetData(id) {
	await func(getDataFirst, id, 2500);
	await func(getDataSecond, id, 1500);
	await func(getDataThird, id, 1000);
}

这样,是不是就有点同步的思维了,符合人的编程习惯,不需要考虑回调和那些繁琐的异步执行了。

开始实验

启动服务
在这里插入图片描述

index.html页面

在这里插入图片描述
PS:我不是前端,只是学习了几个标签,所以界面就很丑,不过只是验证一下功能,也就不重要了!

非同步异步加载

在这里插入图片描述

同步异步加载

在这里插入图片描述

说明

这里我是参考了网上的许多资料,主要是菜鸟教程的资料:
JavaScript 异步编程
JavaScript Promise
因为前端不是我的方向,而且只是为了解决一个小的需求,完全没必要去学习javascript,只需要看需要的知识就好了。所以,这里可能就有点不求甚解了,只知其然,不知其然知其所以然!如果想要深入学习的话,可以去参考上面两篇博客或者其它教程。

PS:从职业发展角度来看,学习js还是非常必要的!如果只学习自己需要的部分,那么能选择的范围就会很狭小了。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值