一、回调、promise、generator、await/async都是为了解决异步的方案
二、回调函数的弊端
如果有多层嵌套的回调,就会出现令人恐惧的三角形
为了解决上面的三角问题,出现了promise
三、promise
promise简单来说就是一个容器,里面保存着某个未来才会结束的时间(通常是一个异步操作的结果),未来使用then获得结果;这样就解决了回调三角形:
下面说明promise是一个容器:
var promise = new Promise(function(resolve, reject) {
// 异步处理
// 处理结束后、调用resolve 或 reject
});
补充:Promise.all 与 Promise.race的妙用
Promise.all 接收一个数组,数组的每一项都是一个promise对象。当数组中所有的promise的状态都达到resolved的时候,Promise.all的状态就会变成resolved,如果有一个状态变成了rejected,那么Promise.all的状态就会变成rejected(任意一个失败就算是失败),这就可以解决我们并行的问题。调用then方法时的结果成功的时候是回调函数的参数也是一个数组,按顺序保存着每一个promise对象resolve执行时的值。
看结果不难看出来符合之前所说的
Promise.race 竞速模式 也是接受一个每一项都是promise的数组。但是与all不同的是,第一个promise对象状态变成resolved时自身的状态变成了resolved,第一个promise变成rejected自身状态就会变成rejected。第一个变成resolved的promsie的值就会被使用。
Promsie.race还有一个很重要的实际用处就是,有时候我们要去做一件事,但是超过三秒钟左右我们就不做了那怎么办?这个时候可以使用Promise.race方法
Promise.race([promise1,timeOutPromise(3000)]).then(res=>{})// timeOutPromise延时3s左右 由于是用setTimeout来实现的并不一定准确3s(一般主线程在开发中不会阻塞3s以上的所以不会有太大问题)这就是我对于Promise的一些基本理解。
promise的then方法获得结果的弊端
如果嵌套多层,那么很多then,也是很可怕的,这是就出现了async和await
我们都知道已经有了Promise的解决方案了,为什么还要ES7提出新的Async/Await标准呢?
答案其实也显而易见:Promise虽然跳出了异步嵌套的怪圈,用链式表达更加清晰,但是我们也发现如果有大量的异步请求的时候,流程复杂的情况下,会发现充满了屏幕的then,看起来非常吃力,而ES7的Async/Await的出现就是为了解决这种复杂的情况。
四、async和await
async和await本质是一种替换promise使用then来获得结果的替代方案
我们常说什么async/await的出现淘汰了Promise,可以说是大错特错,恰恰相反,正因为有了Promise,才有了改良版的async/await,从上面分析就可以看出,两者是相辅相成的,缺一不可。
async和await常用案例
//获取到城市信息
getLocation(phoneNum) {
return axios.post('phoneLocation', {
phoneNum
})
}
// 点击确定按钮时,获取面值列表(返回值为promise可以用then接受)
getFaceResult () {
this.getLocation(this.phoneNum)
.then(res => {
if (res.status === 200 && res.data.success) {
let province = res.data.obj.province;
let city = res.data.obj.city;
this.getFaceList(province, city)
.then(res => {
if(res.status === 200 && res.data.success) {
this.faceList = res.data.obj
}
})
}
})
.catch(err => {
console.log(err)
})
}
//使用async和await(对于返回promise的方法,可以在前面加一个await来获得结果,而且await下面的方法会等有返回值才执行)
async getFaceResult () {
try {
let location = await this.getLocation(this.phoneNum);
if (location.data.success) {
let province = location.data.obj.province;
let city = location.data.obj.city;
let result = await this.getFaceList(province, city);
if (result.data.success) {
this.faceList = result.data.obj;
}
}
} catch(err) {
console.log(err);
}
}
更详细的资料:https://segmentfault.com/a/1190000016788484?_ea=4854890
五、Generator函数
①async和await是generator函数的语法糖
②最大特点就是可以交出函数的执行权(即暂停执行)===>异步操作需要暂停的地方,都用 yield 语句注明
function* gen(x){
var y = yield x + 2;
return y;
}
上面代码就是一个 Generator 函数。它不同于普通函数,是可以暂停执行的,所以函数名之前要加星号,以示区别。
③generator函数执行方式是调用next函数
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器 )g 。这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。调用指针 g 的 next 方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的 yield 语句,上例是执行到 x + 2 为止。
换言之,next 方法的作用是分阶段执行 Generator 函数。每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)。value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。
注意点说明!!!通过下面的两套代码返回值,可知。执行next方法后返回的是对象,包含value和done;注意如果next的参数为空,这个返回的对象不会直接复制给yield左边的变量(所以下面的第一套代码y为undefined);注意如果next中有参数的时候,这个参数会作为左边的参数的值(所以下面第二套代码y为2);总言之,如果使用next没有传入参数,此时yield表达式的左边值为undefined;
function *gen1(x){
var y = yield x + 2;
return y;
}
var g = gen1(1);
第一套执行:
g.next() // { value: 3, done: false }
g.next() // { value: undefine, done: true }
第二套执行:
g.next() // { value: 3, done: false }
g.next(2) // { value: 2, done: true }
generator实战代码
var fetch = require('node-fetch');
function* gen(){
var url = 'https://api.github.com/users/github';
var result = yield fetch(url);
console.log(result.bio);
}
上面代码中,Generator 函数封装了一个异步操作,该操作先读取一个远程接口,然后从 JSON 格式的数据解析信息。就像前面说过的,这段代码非常像同步操作,除了加上了 yield 命令。
var g = gen();
var result = g.next();
result.value.then(function(data){
return data.json();
}).then(function(data){
g.next(data);
});
上面代码中,首先执行 Generator 函数,获取遍历器对象,然后使用 next 方法(第二行),执行异步任务的第一阶段。由于 Fetch 模块返回的是一个 Promise 对象,因此要用 then 方法调用下一个next 方法。
可以看到,虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)