网站首页 > 技术文章 正文
目前,JavaScript的编程元素相比一些主流的编程语言比如C#和Java还是不算多,但是应用它的语法糖很多,如果不熟悉这些语法糖,很多的代码将难以阅读与理解,就比如同样是链式编程,在代码组织上,C#上就比JavaScript来得简明,也可能是个人习惯,觉得JavaScript以后容纳更多的编程元素后将更难以阅读,但它的发展还远没有到头,按现在的发展趋势,今后它有可能成为一流的、应对所有应用的最佳开发工具。
对于异步编程,就是通常我们俗称的AXAJ即Asynchronous Javascript And XML(异步JavaScript和XML),我们都认为它一种具体的编程技术,实质上,我们应该理解AXAJ为一系列的支持异步编程的方法、工具的集合(也可以说是一种思想),这个集合里面有很多的对象和方法来帮助我们用JavaScript语言实现异步编程,比如XMLHTTPRequest、JQuery里面的AXAJ、Promise、Fetch、Axios、async/await等等,Javascript语言本身是不支持异步编程的,但JavaScript的宿主浏览器支持,所以Javascript通过回调函数将需要异步处理的调用放到浏览器的事件循环队列,这样就实现了异步编程。
我们怎样使用JavaScript来实现异步编程的呢?
一、网页嵌套、定时器
1998年,我使用HTML + CGI开发一个Web应用,使用Web页面框架来实现局部刷新,程序提交数据时候将调用页面底部的响应而提交按钮所在的页面,这样只是底部显示变化数据【其他页面如有数据变化,这个页面的处理程序也一样可以处理】而整个页面不动。
使用定时器来实现异步编程在以前也用到过,类似在一个消息队列中不断加入消息检测,满足条件了就执行处理函数。
这些都太老旧了。
二、XMLHTTPRequest
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XMLHttpRequest</title>
</head>
<body>
<script>
//1、创建Ajax对象
let xhr=new XMLHttpRequest();
//2、设置请求信息
//xhr=open(请求方式,请求地址,是否异步)
xhr.open('Post','http://127.0.0.1/ES6_Base/ASD.php',true);
//3、配置请求完成事件
xhr.setRequestHeader('content-type','application/x-www-form-urlencoded')
xhr.onreadystatechange=function(){
if(xhr.readyState===4){
if(xhr.status>=200 && xhr.status<300){
let res=JSON.parse( xhr.responseText );
console.log( "用户名:"+res.Name );
console.log( "密码:"+res.Password );
}else{
console.log(xhr.status);
}
}
}
//4、发送请求
xhr.send("Name=ASD&Password=123");
</script>
</body>
</html>
PHP脚本:
<?php
$returnArr=[];//准备返回的数组
$Name = $_POST['Name'];
$Password = $_POST['Password'];
$returnArr['Name']=$Name;
$returnArr['Password']=$Password;
header('Content-type:text/json');
echo json_encode($returnArr,JSON_UNESCAPED_UNICODE);
?>
返回结果:
用户名:ASD
密码:123
最开始的时候,大家都习惯使用这样的编码来实现异步编程,但这样原生的编码方式不方便,如果有嵌套那么代码也显得冗长,不方便阅读,现在几乎没有人再使用它。
Jquery的出现让编程方式做了很大的改变。
三、JQuery的AXAJ方法(函数)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="jquery-3.4.1.min.js" charset="utf-8"></script>
<title>Jquery</title>
</head>
<body>
<script>
$.ajax({
url:'http://127.0.0.1/ES6_Base/ASD.php',
data:{
"Name":"ASD",
"Password":"123"
},
type:'POST',
async:true,
success:function (JsonData) {
console.log("用户名:"+JsonData['Name']);
console.log("密码:"+JsonData['Password']);
}
});
</script>
</body>
</html>
返回和上面一样的结果。
JQuery也是封装了原生AXAJ的API,使用起来方便快捷。
在很多框架里可以看到JQuery的身影,它们都集成了JQuery,比如LayUI、EasyUI等。
JQuery自2006年诞生起在很长一段时间里相当流行,是当时使用最为广泛和频繁的函数库,但目前逐渐在淡出。主要原因可能是回调地狱、ECMAScript6下有更好的异步编程模式等等。
四、Promise对象
Promise是ECMAScript6中提供的用于解决异步编程的类。
使用它有助于书写优雅、复杂的异步任务,改善了异步编程的困难,避免了回调地狱,比传统的解决方案回调函数和事件更合理和更强大。
通俗地理解,就是使用同步编程的写法可以解决异步编程的问题。
const p=new Promise( (resolve,reject)=> {
//执行任务
//任务成功时执行resolve,可以包含结果
//任务失败时执行reject,可以包含错误提示信息
}).then( ( value )=>{
//获取resolve的结果再进一步进行处理
}).catch( ( reason )=>{
//获取reject的信息并进一步处理
})
上面是我们经常使用的写法,也可以使用下面的写法,效果与上面一样。
const p=new Promise( (resolve,reject)=> {
//执行任务
//任务成功时执行resolve,可以包含结果
//任务失败时执行reject,可以包含错误提示信息
}).then( ( value )=>{
//获取resolve的结果再进一步进行处理
} ,( reason )=>{
//获取reject的信息并进一步处理
})
注意点:
(1) promise对象有三种状态,分别是pending(准备中)、fulfilled(成功/完成)、rejected(拒绝/失败)。
(2) promise对象的状态改变是一次性的,也就是只能改变一次,要么pending -> fulfilled,要么pending -> rejected。
(3) 通过执行resolve改变promise对象状态由pending -> fulfilled,执行reject改变promise对象状态由pending -> rejected。
(4) then方法返回的是promise对象,promise支持方法调用的链式写法。
(5) 当promise的状态为rejected或者执行体中的代码异常时执行catch函数。
虽然promise相比Jquery的Axaj有很大的改观,的确也解决了异步编程中常遇到的回调地狱、回调函数的嵌套问题,但实际写代码过程中,依然觉得promise如果then很多的情况下阅读代码不是很方便。
promise在现在的异步编程中应用相当普遍。
五、asyc/await
比较而言,ECMAScript7(2016年)中应用于异步编程的asyc/await则人性化了许多,真正实现了异步处理同步化(写法)。
async GetListTask(id){
let result;
result=await this.GetListTask1(id) //等待获取任务列表1
//......处理
result=await this.GetListTask2(id) //等待获取任务列表2
//......处理
}
注意点:
(1) 如果函数中包含了await但函数前面没有冠以async则程序会报错。
(2) 函数前面直接使用asyc但函数体内无await程序并不会报错,但无意义,asyc/await必须成对出现才有意义。
现在很多设计异步编程的的开发都使用asyc/await。
六、Generator(生成器)
通过生成器返回的迭代器对象,根据需要执行next()方法来控制程序进度。
function* ToDo() {
console.log("执行第一步任务");
yield 111;
console.log("执行第二步任务");
yield 222;
console.log("执行第三步任务");
}
const myToDo = ToDo(); //获取生成器对象
console.log(myToDo.next().value); // 输出:111,到yield 111为止
console.log(myToDo.next().value); // 输出:222,到yield 222为止
console.log(myToDo.next().value); // 输出:undefined,任务执行完毕
输出结果:
执行第一步任务
111
执行第二步任务
222
执行第三步任务
undefined
通过迭代来控制:
for(let item of myToDo) {
console.log( item )
}
结果输出:
执行第一步任务
111
执行第二步任务
222
执行第三步任务
这种方式有特定的应用场景,感觉还是习惯asyc/await。
七、fetch对象
fetch被称为下一代的Ajax技术,采用promise方式来处理数据,代码结构比较简洁,fetch不是对XMLHTTPRequest对象的进一步的封装,是新的异步编程实现方法和规范。
fetch默认不会带有cookie,需要添加配置项,fetch不能再请求过程中检测进度。
fetch('http://127.0.0.1:8018/JS/ECMAScript6/ES6_Base/ASD.php',{method:'post',body:'Name=ASD&Password=123',headers:{ 'Content-Type':'application/x-www-form-urlencoded' } }).then(data=>{
//text()是fetch的一部分,它返回的是promise对象,用于获取后台返回的数据
return data.text();
}).then(result=>{
//这里获得最终的数据
console.log(result);
})
结果输出:
{"Name":"ASD","Password":"123"}
八、Axios
Axios是一个基于Promise、用于浏览器和Node.js的HTTP客户端,它也是对原生XMLHTTPRequest对象的promise封装。这个异步编程元素必须重点掌握。
1、axios的常用方法
⑴. 发送 GET 请求:axios.get(url[, config])。
⑵. 发送 POST 请求:axios.post(url[, data[, config]])。
⑶. 发送 PUT 请求:axios.put(url[, data[, config]])。
⑷. 发送 DELETE 请求:axios.delete(url[, config])。
⑸. 发送 HEAD 请求:axios.head(url[, config])。
⑹. 发送 OPTIONS 请求:axios.options(url[, config])。
⑺. 发送 PATCH 请求:axios.patch(url[, data[, config]])。
axios 提供的各种方法分别对应HTTP规范中定义的不同请求方法,它们各自有不同的用途,通常与 RESTful API 设计标准保持一致,也是为了让axios操作更加语义化。
对应的后端操作(NodeJS):
constexpress=require('express');
constmyapp=express();
myapp.get('/api',(req,res)=>{
//处理get请求
});
myapp.post('/api',(req,res)=>{
//处理post请求
});
myapp.put('/api',(req,res)=>{
//处理put请求
});
myapp.delete('/api',(req,res)=>{
//处理delete请求
});
myapp.patch('/api',(req,res)=>{
//处理patch请求
});
myapp.head('/api',(req,res)=>{
//处理head请求
});
myapp.options('/api',(req,res)=>{
//处理OPTIONS请求
});
在平常的开发中,我们经常使用的是get和post方法,通常是在这两个操作中完成对数据的增、删、改、查。
2、axios的编码范式
axios是一个基于Promise的HTTP客户端,在JavaScript代码中使用axios库时,有几种常见的编码范式。
以axios的get方法为例,下面是常见的用法。
⑴基本使用
axios.get('http://127.0.0.1:1234/user')
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.error(err);
});
axios({
url:'http://127.0.0.1:1234/user',
method:'get'
}).then(function (res) {
console.log(res);
})
.catch(function (err) {
console.error(err);
});
⑵带有参数
axios.get('http://127.0.0.1:1234/user', {
params: {
ID: 2
}
})
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.error(err);
});
axios({
url:'http://127.0.0.1:1234/user',
method:'get',
params: { ID: 2 }
})
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.error(err);
});
⑶通过async/await使用
async function getUserInfo() {
try {
const res = await axios.get('http://127.0.0.1:1234/user?ID=2');
console.log(res);
} catch (err) {
console.error(err);
}
}
getUserInfo();
⑷创建axios实例
const myAxios = axios.create({
baseURL: 'http://127.0.0.1:1234',
timeout: 2000,
});
myAxios.get('/user?ID=2')
.then(function (res) {
console.log(res);
}).catch(function (err) {
console.error(err);
});
3、axios的链式编程
很多前端开发者喜欢链式编程写法。
链式编程中如果任何一个请求发生错误,最后的catch方法都将捕获并统一处理错误,这是方便的地方,但是过多的链式编程给调试带来不必要的麻烦。
Mock.mock('http://127.0.0.1:1234/user?name=vue',{
'user':{'Id':1 }
})
Mock.mock('http://127.0.0.1:1234/user?userId=1',{
'info':{'title':'vue + elementUI + Admin' }
})
axios.get('http://127.0.0.1:1234/user?name=vue')
.then(function (res) {
return res.data.user.Id;
})
.then(function (userId) {
return axios.get('http://127.0.0.1:1234/user?userId='+userId);
})
.then(function (res) {
console.log(res.data.info.title);
})
.catch(function (err) {
console.err(err);
});
程序最后输出:vue + elementUI + Admin。
4、axios处理并发请求
Mock.mock('http://127.0.0.1:1234/getUserName',{
'name':'vue'
})
Mock.mock('http://127.0.0.1:1234/getUserInfo',{
'Framework':'vue + elementUI + Admin'
})
function getUserName() {
return axios.get('http://127.0.0.1:1234/getUserName');
}
function getUserInfo() {
return axios.get('http://127.0.0.1:1234/getUserInfo');
}
Promise.all([getUserName(), getUserInfo()])
.then(function (res) {
const arr1 = res[0];
const arr2 = res[1];
// 处理结果
console.log(arr1.data.name);
console.log(arr2.data.Framework);
});
axios.all([getUserName(), getUserInfo()])
.then(function (res) {
const arr1 = res[0];
const arr2 = res[1];
// 处理结果
console.log(arr1.data.name);
console.log(arr2.data.Framework);
});
如果不使用axios.all()或者Promise.all()来执行,让两个axios单独执行,那么可能由于axios完成时间的不同,导致不同的axios获取结果存在先后顺序;但是放在axios.all()或者Promise.all()中来执行,那么就是等待这两个都完成再一起输出结果。
现在流行的写法是使用箭头函数:
axios.all([getUserName(), getUserInfo()])
.then(res=>{
console.log(res);
for(let i=0;i<res.length;i++){
console.log(res[i].data);
}
});
或者:
axios.all([getUserName(), getUserInfo()])
.then(axios.spread(
(res1,res2) => {
console.log(res1.data.name);
console.log(res2.data.Framework);
}));
5、创建axios实例
一般写法:
const myAxios=axios.create();
myAxios.defaults.baseURL="http://127.0.0.1:1234";
myAxios.request({
method:'get',
url:'/user',
timeout: 2000,
headers: {'X-Custom-Header': 'foobar'}
}).then(
res=>{ console.log(res.data.name); }
)
或者:
const myAxios=axios.create({
baseURL:"http://127.0.0.1:1234",
timeout: 2000,
headers: {'X-Custom-Header': 'foobar'}
});
myAxios.request({
method:'get',
url:'/user',
}).then(
res=>{ console.log(res.data.name); }
)
6、axios拦截器
拦截操作就是在axios进行操作之前进行一些系统需要统一处理的操作,比如可以验证用户、检查请求的合法性、检查数据的完整性等等。
Mock.mock('http://127.0.0.1:1234/user',{
'name':'JavaScript'
})
const myAxios=axios.create({
baseURL:"http://127.0.0.1:1234",
timeout: 2000,
headers: {'X-Custom-Header': 'foobar'}
});
myAxios.interceptors.request.use(
request=>{
console.log("检查请求...");
return request
}
)
myAxios.interceptors.response.use(
response=>{
console.log("检查返回数据...");
return response
}
)
myAxios.request({
method:'get',
url:'/user',
}).then(
res=>{ console.log(res.data.name); }
)
上面执行的结果显示如下:
检查请求...
检查返回数据...
JavaScript
这篇文章合成了我的CSDN博客上的两篇文章《AXAJ与JavaScript中的异步编程元素》和《axios》。
猜你喜欢
- 2024-10-05 聊聊浏览器的事件循环 浏览器循环点击插件
- 2024-10-05 vue 基础-生命周期 lifecycle 的执行顺序和作用
- 2024-10-05 前端如何搞监控总结篇 前端实时监控界面
- 2024-10-05 JavaScript setTimeout要理解 js中settime
- 2024-10-05 描述React的组件生命周期方法,并解释它们在何时被调用。
- 2024-10-05 面试必备-setTimeout vs setInterval哪个更准确,0ms延迟的真相
- 2024-10-05 autolog.js:一个小而美的toast插件。
- 2024-10-05 JavaScript 事件循环:理解进程、线程和异步编程
- 2024-10-05 高级前端进阶,你了解事件循环吗?
- 2024-10-05 「中高级前端」高性能渲染十万条数据(时间分片)
你 发表评论:
欢迎- 05-10如何优化数据库和前端之间的交互?
- 05-10前端代码优化小秘籍(前端优化24条建议)
- 05-10VS Code当中的15个神仙插件,值得收藏
- 05-10如何自己开发一个Google浏览器插件?
- 05-10前端流行框架Vue3教程:14. 组件传递Props效验
- 05-10吃了一年的SU,最好用的插件都在这了
- 05-10前端必看!这款神器让网站界面告别千篇一律
- 05-10程序员请收好:10个非常有用的 Visual Studio Code 插件
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端md5加密 (49)
- 前端路由 (55)
- 前端数组 (65)
- 前端定时器 (47)
- 前端懒加载 (45)
- 前端接口 (46)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle查询数据库 (45)
- oracle约束 (46)
- oracle 中文 (51)
- oracle链接 (47)
- oracle的函数 (57)
- mac oracle (47)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)