目的:总结
应用场景:一部分数据是要等个别接口请求返回数据后再进行调用请求
**1.**在function1 执行完成之后再调用function2 但如果嵌套调用层数较多,就会出现‘回调地狱’,如下:
//普通的
function A(callback){
console.log("I am A");
callback(); //调用该函数
}
function B(){
console.log("I am B");
}
A(B);
//回调地狱
function fun1(a) {
a();
}
function fun2(b) {
b();
};
function fun3(c) {
c();
};
function fun4(d) {
d();
};
function fun5(e) {
e();
};
function fun6() {
console.log("6");
}
fun1(() => {
fun2(() => {
fun3(() => {
fun4(() => {
fun5(() => {
fun6();
})
})
})
})
});
2.ES6 的 Promise
Promise可以很好的解决上述的回调地狱问题,更清晰的表达回调
Promise对象有以下两个特点。
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
//基于Promise 封装ajax
function ajax({url:"",type:"get",dataType:"json"}) {
return new Promise((resolve,reject)=>{
let xhr=new XMLHttpRequest();
xhr.open(type,url,true);
//后台给返回的数据类型,一般都是json
xhr.responseType=dataType;
xhr.onload=function(){
if(xhr.code ==200 && xhr.readyState ==4){
//成功调用
resolve(xhr.response)
}
}
xhr.onerror=function(err){
//失败调用
reject(err)
}
xhr.send();
})
}
//基于promise封装的axios
export const get/post = (url,params)=>{
params = params || {};
return new Promise((resolve,reject)=>{
// axios自带 get 和 post 方法
$http.get/post(url,{
params,
}).then(res=>{
if(res.data.status===0){
resolve(res.data);
}else{
alert(res.data.msg)
}
}).catch(error=>{
alert('网络异常');
})
})
}
//Eg:vue项目中所用到的基于promise原理实现的请求接口
handlerClose(id,k) {
this.$axios({
method:'post',
headers: { //请求头
'Content-Type': 'application/json',
"Authorization": 'Bearer '+this.accessToken //token换成从缓存获取
},
url:this.videourl+'v1/live/end-live',
data:{
liveId:id
}
}).then( res =>{
if(res.data.code==200){
this.$toast('关闭成功');
this.list.splice(k,1);
}else{
this.$toast(res.data.message);
}
}).catch( e =>{
let code=e.response.status;
if(code==401){
this.islogin=true;
}
})
},
3.ES6 的Generator
Generator(生成器)是一类特殊的函数,跟普通函数声明时的区别是加了一个*号,以下两种方式都可以得到一个生成器函数:
//2种基础写法
function* helloWorldGenerator{
//to do something
}
function *helloWorldGenerator{
//to do something
}
Iterator(迭代器):当我们实例化一个生成器函数之后,这个实例就是一个迭代器。可以通过next()方法去启动生成器以及控制生成器的是否往下执行。
yield/next:这是控制代码执行顺序的一对好基友。
通过yield语句可以在生成器函数内部暂停代码的执行使其挂起,此时生成器函数仍然是运行并且是活跃的,其内部资源都会保留下来,只不过是处在暂停状态。
在迭代器上调用next()方法可以使代码从暂停的位置开始继续往下执行。
Eg://next()第一次调用是启动生成器
function *helloWorldGenerator(){
console.log('第一次打印starting helloWorldGenerator()');
yield;
console.log('第二次打印starting helloWorldGenerator()');
yield;
console.log('第三次打印starting helloWorldGenerator()');
let _self=helloWorldGenerator();
_self.next();
_self.next();
_self.next();
/*
打印结果:
第一次打印starting helloWorldGenerator()
第二次打印starting helloWorldGenerator()
第三次打印starting helloWorldGenerator()*/
yield 表达式
由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。
遍历器对象的next方法的运行逻辑如下。
(1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
(3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
(4)如果该函数没有return语句(函数末尾如果没有return,就是隐含的return undefined;),则返回的对象的value属性值为undefined。
1、function *helloWorldGenerator(){
console.log('第一次打印starting helloWorldGenerator()');
yield 123+456;
}
let res=helloWorldGenerator();
res.next();
let result=res.next();
console.log('value',result.value); //值为undefined,原因没有retrun语句
2、function *helloWorldGenerator(){
console.log('第一次打印starting helloWorldGenerator()');
yield;
yield 123+456;
return 123+456
}
let res=helloWorldGenerator();
res.next();
let result=res.next();
console.log('value',result.value); //value 579
使用generator实现的请求
function getCallSettings({url:"",type:"get",dataType:"json"}) {
return new Promise((resolve,reject)=>{
let xhr=new XMLHttpRequest();
xhr.open(type,url,true);
//后台给返回的数据类型,一般都是json
xhr.responseType=dataType;
xhr.onload=function(){
if(xhr.code ==200 && xhr.readyState ==4){
//成功调用
resolve(res=>{
it.next(res.dialerSetting); // 将res.dialerSetting传给yield表达式
});
}
}
xhr.onerror=function(err){
//失败调用
reject(err=>{
it.throw(err); // 抛出错误
})
}
xhr.send();
}
function *dealData() {
try{
let settingInfo = yield getCallSettings();
//to do something……
}
catch(err) {
console.log(err); // 捕捉错误
}
}
let it = dealData();
it.next(); // 第一次调用next()是启动生成器
4.ES7 的async和await
async函数对 Generator 函数的改进,体现在以下四点
(1)内置执行器。generator需要第一次调用next()来启动执行
(2)更好的语义。async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果
(3)更广的适用性。co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
(4)返回值是 Promise。async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。
async的多种声明形式
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)
async函数返回一个promise,async函数内部return语句返回的值,会成为then方法回调函数的参数。
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
async function f() {
// 等同于
// return 123;
return await 123;
}
f().then(v => console.log(v))
// 123
async中使await fun()先执行,等到三秒后执行完再把得到的结果赋值给左边的n,也就是说test函数需要三秒钟才执行完成,所以test函数是异步的,因此前面必须写async,如下
function fun(){
return new Promise((resolve, reject)=>{
let sino = parseInt(Math.random() * 6 +1)
setTimeout(()=>{
resolve(sino)
},2000)
})
}
async function test(){
let n =await fun()
console.log('n',n)
}