众所周知js是单线程,从头跑到尾然后不停的事件循环,但是浏览器是多线程的,所有前端所有的异步可以归纳为,js的事件循环在根据标准不停的前后执行不同异步线程的回调
其实现在js对于异步的所有解决方案不管是async 还是 promise 还是监听什么的归根结底都是回调触发,而我们至今为异步所作的所有努力不过是让异步回调写的更加像同步一些
目前前端对异步的处理大致经过这三个阶段
1 纵向无限回调,比如ajax的回调
2 promise 通过then链式回调,比如axios等
3asyns 通过promise在resolve中交换控制权来自动执行的迭代器来让异步变得看起来像同步
无限回调就不讲了,每个回调函数之前相互依赖形成强耦合从而使代码纵向发展变成一坨代码
直接从promise开始,promise其实就是回调换了一种写法而已,也就是对回调多了一层封装
简单来讲就是他是一个原生提供的js对象,接受一个函数作为参数来生成实例,这个函数又以resolve,reject这两个函数作为参数
一共有三种状态分别是进行中(pending)已解决(fulfilled)已失败(rejected)
然后我们平常说的什么把一个异步回调封装成promise什么的听起来高大上不过就是,你自己根据回调函数的结果,自行选择执行resolve(将状态从pending转换成fulfilled)或者reject(将状态从pending转换成rejected)
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
然后他有一个最重要也是用的最多的一个方法then()他接受两个函数作为参数分别作为上面说的resolve,reject执行完成后的回调参数,而参数则是则是由resolve传入
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
console.log('结果:')
resolve('123');
} else {
reject('321');
}
});
promise.then(x=>{
console.log(x)
},y=>{
console.log(y)
}
)
//结果:
123
321
同时then()也是返回一个promise对象所以可以继续链式回调,上一个then()方法return的参数可以作为下一个then()方法的参数
除此之外还有all(),race()之类的辅助方法,都是些顾名思义的api
其实从本质上来说promise就是回调换了一种写法,在回调发生之时去修改promise的内部状态,然后通过不同的状态,来去决定执行then()里面的哪个方法,当业务逻辑复杂起来的时候,你特么不照样不停的then(),也很丑啊!
所以出现了async 和 await 写起来感觉和同步差不多,又美观又好看,妈妈再也不用担心我代码丑了,虽然看起来高大上其实本质还是回调,在我看来最大的好处不过是开发体验的提升而已
首先要明白的是 async 不过是 Generator的语法糖,而Generator本质是个不能自动执行的迭代器,那么问题来了什么是js的迭代器呢?
迭代器本质是来说是一个指针对象指向数据结构的起始位置,通过调用next()方法使其指向下一个存储单元,且访问该数据的描述信息
像在js中[]就原生装载了遍历器接口,访问数组实例的[Symbol.iterator]属性,便会返回该数据结构的遍历器对象,然后调用这个对象的next()方法便会依次返回该数据结构各个位置的描述信息
let a = [1,2]
let i = a[Symbol.iterator]();
console.log(i.next())
返回:{value: 1, done: false}
Generator 函数,便是返回这么一个遍历器对象,通过调用.next()方法来返回他内部保存的状态,所以你也可以叫他状态保存机
function* generator(){
yield 1;
yield 2;
return 3;
}
let g = generator()
g.next()
返回 { value: 1, done: false }...
他之所以能被用在异步应用中的一个关键属性是:你可以通过next()传值来作为上一步操作所产生的结果值,这样我们就可以通过外部输入的参数值不同来对Generator的执行进行干预,从而达到交换执行权的效果
function* generator(){
yield 1;
let x = yield 2;
console.log(x)
return 3;
}
let g = generator()
g.next(5)
返回 { value: 1, done: false },5
那我们要做的就是让Genrator自动执行,一直到最后一个状态,比如这样:
function* generator(){
yield 1;
yield 2;
return 3;
}
let g = generator()
if(!g.next().done){
g.next()
}
但是这样做不到我们想要的异步操作,我们想要的效果是,在上一步异步操作执行成功的后执行下一步异步操作,那么我们的思路就是提供一个方法,让他可以自动的执行generator,在每一个异步操作通过yield语句将函数的执行权过度给异步方法,同时generator外部我们可以通过获取到异步函数的返回结果来决定是否启用下一步,如果异步执行成功,我们再将函数的执行权交还给generate方法。之前我们说过promise,那么如果我们把每一个异步操作都封装成promise对象,这样我们就可以尝试着写一个自动执行的方法
function run(gen){
var g = gen();
function next(data){
var result = g.next(data);
if (result.done) return result.value;
result.value.then(function(data){
next(data);
});
}
next();
}
run(gen);
因为yield返回的是一个Promise对象,那么我们在调用next()方法时将获取到这个对象,之前我们说过promise的then方法接受两个方法作为参数分别作为执行成功和执行失败的回调函数,那么我们就只需要在执行成功的那个方法也就是第一个传入的函数执行next()方法那么如果每一步异步操作都正常执行的话那么整个generator方法将自动执行,async机制就是自带了一个可以判断promise状态的执行器,可以在await的时候将resolve的值给return 回来