JS异步函数顺序执行总结

目的:总结
应用场景:一部分数据是要等个别接口请求返回数据后再进行调用请求
**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属性值。

扫描二维码关注公众号,回复: 12609084 查看本文章

(4)如果该函数没有return语句(函数末尾如果没有return,就是隐含的return undefined;),则返回的对象的value属性值为undefined。

    1function *helloWorldGenerator(){
    
    
    console.log('第一次打印starting helloWorldGenerator()');
    yield 123+456;
   }
	let res=helloWorldGenerator();
	res.next();
	let result=res.next();
	console.log('value',result.value);  //值为undefined,原因没有retrun语句
   2function *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)
    }

猜你喜欢

转载自blog.csdn.net/weixin_46918658/article/details/105531025
今日推荐