受
javascript
语言特性的影响,编程过程中充斥着大量异步回调,这会让代码维护起来特别麻烦,一步步走向回调地狱。社区中最早提出Promise
解决方案,es6
将其融入语法标准,并提供了generator
、async
,向类同步编程不断努力。本文会通过这三个方面演示类同步进化过程。
1.Promise
Promise
提供异步编程的容器,包含异步代码,在得到异步结果时,通过resolve
传递数据(resove
对应then
所指定的函数,其实也就是单个过程的异步回调,可以理解成将之前的回调函数放在then
方法中定义)。
以ajax请求封装为例:
- 传统形式
function ajax(url, success) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.status == 200 && xhr.readyState == 4) {
//将请求结果作为实参传入成功回调
success(xhr.responseText);
}
}
}
//如果两个异步过程有先后顺序,则会出现这种嵌套情况
ajax("http://vebcoder.cn:9527/api/getTypeOne", function (res) {
console.log(res);
ajax("http://vebcoder.cn:9527/api/goodList", function (res) {
console.log(res);
})
})
- Promise形式
function ajax(url) {
//promise容器包裹异步过程
return new Promise(resolve => {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.status == 200 && xhr.readyState == 4) {
//将异步结果使用resolve进行传递
resolve(xhr.responseText);
}
}
})
}
ajax("http://vebcoder.cn:9527/api/getTypeOne")
.then(res=>{
console.log(JSON.parse(res))
})
.then(()=>{
return ajax("http://vebcoder.cn:9527/api/goodList")
})
.then(res=>{
console.log(JSON.parse(res))
})
解决了回调函数横向发展的问题,变成了纵向发展结构。直观确实很直观但是一大堆的then
方法,像风一样掠过你的天灵盖!接下来generator
登场
2.generator
乍一看,generator
不过是一个有多个返回值的函数而已,奥妙在于如果不调用next
方法,代码会停止执行的。
- 基础用法
//function后加*
function* Gen(){
console.log(1);
yield;
console.log(2)
yield;
console.log(3);
return;
}
//调用函数获得指针对象
var g=Gen();
g.next();//1
g.next();//2
g.next();//3
第一次调用next
执行到第一个yield
,第三次执行到函数末尾return
的位置。
- 奥妙之处
function* Gen(){
//用变量接收yield命令
var res=yield;
console.log(res)
}
var g=Gen();
g.next();
//next传入参数
g.next(100);//100
第一次调用next
方法,代码执行到yield
停止执行。第二次调用next
传入参数,代码继续执行,并将传入next
的参数赋值给res
变量。next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值。
那么我们就可以这样做:
//上述封装的ajax方法-传统形式
function* Gen(){
//请求成功开始下一步
ajax("http://vebcoder.cn:9527/api/getTypeOne",res=>{g.next(res)})
// 接收yield返回值
let res=yield;
console.log(res)//请求的数据结果
}
var g=Gen();
// 开始执行代码
g.next();
//上述封装的ajax方法-Promise形式
function* Gen(){
//请求成功开始下一步
ajax("http://vebcoder.cn:9527/api/getTypeOne").then(res=>{g.next(res)})
// 接收yield返回值
let res=yield;
console.log(res)//请求的数据结果
}
var g=Gen();
// 开始执行代码
g.next();
使用口诀:上一步回调,下一步,接收yield
等待结果传入
按理说这种形式已经不错了,不用再往下看了,除非你能忍住!
3.async
async
是generator
的语法糖,你只需关注两部、步操作就行。相当于对Promise
和generator
进行了融合。
async function Asy(){
//等待promise实例
let res=await ajax("http://vebcoder.cn:9527/api/getTypeOne")
console.log(res)//请求的数据结果
}
Asy();
//结合自执行函数
;(async function(){
let res=await ajax("http://vebcoder.cn:9527/api/getTypeOne")
console.log(res)//请求的数据结果
}())
async
函数就是将Generator
函数的星号(*
)替换成async
,将yield
替换成await
。
注意:await后面需要跟一个promise实例,无需手动传递结果!
最后来一个对比(有次序的异步过程):
//传统方式(对应上述传统ajax封装形式)
ajax("http://vebcoder.cn:9527/api/getTypeOne", function (res) {
console.log(res);
ajax("http://vebcoder.cn:9527/api/goodList", function (res) {
console.log(res);
})
})
//promise (对应上述promise ajax封装形式)
ajax("http://vebcoder.cn:9527/api/getTypeOne")
.then(res=>{
console.log(JSON.parse(res))
})
.then(()=>{
return ajax("http://vebcoder.cn:9527/api/goodList")
})
.then(res=>{
console.log(JSON.parse(res))
})
//generator (对应上述promise ajax封装形式)
function* Gen(){
//请求成功开始下一步
ajax("http://vebcoder.cn:9527/api/getTypeOne").then(res=>{g.next(res)})
// 接收yield返回值
let res=yield;
console.log(res);
ajax("http://vebcoder.cn:9527/api/goodList").then(res=>{g.next(res)})
// 接收yield返回值
let res2=yield;
console.log(res2);
}
var g=Gen();
// 开始执行代码
g.next();
//async (对应上述promise ajax封装形式)
// 发送请求
;(async function(){
let res=await ajax("http://vebcoder.cn:9527/api/getTypeOne")
console.log(res)//请求的数据结果
let res2=await ajax("http://vebcoder.cn:9527/api/goodList")
console.log(res2)//请求的数据结果
}())
async
有更好的语义,几乎达到与同步代码一样的编程体验!
2019,不过是追求喜新厌旧的年头!