JS异步优化方案Promise、Generator和async/await

参考
https://blog.csdn.net/tcy83/article/details/80427195
https://segmentfault.com/a/1190000007535316#articleHeader5
https://segmentfault.com/a/1190000007032448#articleHeader11

由于JavaScript是单线程的,因此异步编程是非常重要的,在异步操作中有一类很棘手的问题,被称为“回调地狱”,异步的回调函数中,又有另外的异步的回调,一个套一个,再参杂一些同步逻辑判断,代码就会变的难以维护。

//回调地狱
setTimeout(function(){
    
    
   console.log("1 type");
   setTimeout(function(){
    
    
      console.log("2 type");
      setTimeout(function(){
    
    
         console.log("3 type");
         ...
       },1000)
   },1000)
},1000);

解决方法:

1.promise

ES6中的Promise对象用于异步操作,它表示一个尚未完成且预计在未来完成的异步操作。
有三种状态

  • pending:初始值,不是fulfilled,也不是rejected
  • fulfilled:代表操作成功 对应resolve
  • rejected:代表操作失败

状态只能从pending转变为fulfilled,或者从pending转变为rejected。且一旦改变就不会再变了。

 var p = new Promise(function(resolve,reject){
    
    
 	 if (/* 异步操作成功 */) {
    
    
        resolve(data);
    } else {
    
    
        /* 异步操作失败 */
        reject(error);
    }
 	}
p.then(function(value) {
    
    
  // resolved
}, function(error) {
    
    
  // rejected
});

resolve(data):在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。
reject(data):在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
当状态发生变化,promise.then绑定的函数就会被调用。对应的,then方法有两个参数,分别实现resolved,rejected的回调方法,并接受Promise对象传出的值作为参数。

注意:Promise一旦新建就会「立即执行」,无法取消。

  • 通过promise对象和then()的链式调用处理异步
function getPObj(num){
    
    
      var p = new Promise(function(resolve,reject){
    
    
        setTimeout(function(){
    
    
         console.log("开始执行定时器:"+num);
         resolve(num);
       },2000);
      });
      return p;
    }
    getPObj(1).then(function(data){
    
    
       console.log("我是回调方法");
       console.log("执行回调方法:"+data);
       return getPObj(2);
    }).then(function(data){
    
    
       console.log("我是回调方法");
       console.log("执行回调方法:"+data);
       return getPObj(3);
    }).then(function(data){
    
    
       console.log("我是回调方法");
       console.log("执行回调方法:"+data);
    });
  • .catch()
    有的时候我们会需要当回调方法为reject的时候,终止链条,就会用到.catch()方法,该方法是.then(undefined, onRejected)的别名。promise对象的错误,会一直向后传递,直到被捕获。即错误总会被下一个catch所捕获。then方法指定的回调函数,若抛出错误,也会被下一个catch捕获。catch中也能抛错,则需要后面的catch来捕获。
promise.then(function(data) {
    
    
    console.log('success');
}).catch(function(error) {
    
    
    console.log('error', error);
});

/*******等同于*******/
promise.then(function(data) {
    
    
    console.log('success');
}).then(undefined, function(error) {
    
    
    console.log('error', error);
});

//一般用法
sendRequest('test.html').then(function(data1) {
    
    
    //do something
}).then(function (data2) {
    
    
    //do something
}).catch(function (error) {
    
    
    //处理前面三个Promise产生的错误
});
  • .all()

var p = Promise.all([p1, p2, p3]);

该方法用于将多个Promise实例,包装成一个新的Promise实例。

  • Promise.all方法接受一个数组(或具有Iterator接口)作参数,数组中的对象(p1、p2、p3)均为promise实例(如果不是一个promise,该项会被用Promise.resolve转换为一个promise)。它的状态由这三个promise实例决定。

  • 在这里插入图片描述

  • 当p1, p2, p3状态都变为fulfilled,p的状态才会变为fulfilled,并将三个promise返回的结果,按参数的顺序(而不是 resolved的顺序)存入数组,传给p的回调函数.
    当p1, p2, p3其中之一状态变为rejected,p的状态也会变为rejected,并把第一个被reject的promise的返回值,传给p的回调函数。
    在这里插入图片描述

  • .rase()

var p = Promise.race([p1, p2, p3]);

该方法同样是将多个Promise实例,包装成一个新的Promise实例。
Promise.race方法同样接受一个数组(或具有Iterator接口)作参数。当p1, p2, p3中有一个实例的状态发生改变(变为fulfilled或rejected),p的状态就跟着改变。并把第一个改变状态的promise的返回值,传给p的回调函数。
在这里插入图片描述
注意,当race()方法包装的promise实例状态改变后,不会影响其他promise的执行。

2.generator

  • generator(生成器)是ES6标准引入的新的数据类型。generator看上去像一个函数,但可以返回多次。
  • 基本语法
function* text(n){
    
    
	yield n + 1;
    yield n + 2;
    return n + 3;
}

generator由function*定义,通过yield可以多次返回
在这里插入图片描述
控制台输出一下发现,并没有输出123,而是输出了一个对象,这是因为test()只是创建了一个generator对象,还没有去执行它。

  • 调用generator对象有两个方法:
    1.通过generator对象的next()方法
    在这里插入图片描述
    next()方法可以接受参数,用来替换上一次yield执行的结果

2.通过for … of遍历
在这里插入图片描述
由于next()方法是通过ES6的迭代器接口[Symbol.iterator]来实现的,而for-of的原理就是每次遍历都会调用该对象的[Symbol.iterator]属性的next方法,当返回{value: undefined, done: true}后,表示遍历结束。

  • generator的使用
    通过yield和next()可以控制函数的执行与停止,用来处理异步编程。
//使用generator
   function test1(sucess){
    
    
        setTimeout(function(){
    
    
             console.log("1 type");
             sucess();
         },1000)
   }

   function test2(sucess){
    
    
        setTimeout(function(){
    
    
             console.log("2 type");
             sucess();
         },1000)
   }

   function test3(sucess){
    
    
        setTimeout(function(){
    
    
             console.log("3 type");
             sucess();
         },1000)
   }
   
  function run(fn){
    
    
    const gen = fn();
    function next() {
    
    
        const result = gen.next();
        if (result.done) return;//结束
        // result.value就是yield返回的值
        result.value(next);//next作为入参,当前成功后,执行下一步
    }
    next();
  };
  //工序
  function* task(){
    
    
     yield test1;
     yield test2;
     yield test3;
  }
  run(task);//开始执行

3.async和await

  • async 是“异步”的简写,而 await 可以认为是 async wait 的简写。

  • async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。

  • await必须用在async声明的函数中

  • async
    可以看到
    可以看到,这里async函数返回的是一个promise对象,async 函数(包含函数语句、函数表达式、Lambda表达式)会返回一个 Promise 对象,如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。
    这里要注意,声明它是异步,形式是异步,但实际执行并没有异步。
    在这里插入图片描述
    结合 Promise 的特点——无等待,在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,不会阻塞后面的语句。这和普通返回 Promise 对象的函数相同。

  • await
    await一般是在等待一个async函数执行结束的返回值,也就是promise对象。但是也不绝对,它可以等任意表达式的结果,所以,await 后面实际是可以接普通函数调用或者直接量的。
    在这里插入图片描述

await 是个运算符,用于组成表达式,例如 “await test1()”,await 表达式的运算结果取决于它等待的东西。

  • 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

  • 如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。

  • async/await 的使用
    假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。

function takeLongTime(n) {
    
    
    return new Promise(resolve => {
    
    
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    
    
    console.log(`step1 with ${
      
      n}`);
    return takeLongTime(n);
}

function step2(n) {
    
    
    console.log(`step2 with ${
      
      n}`);
    return takeLongTime(n);
}

function step3(n) {
    
    
    console.log(`step3 with ${
      
      n}`);
    return takeLongTime(n);
}

使用async/await

async function doIt() {
    
    
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${
      
      result}`);
    console.timeEnd("doIt");
}

doIt();

猜你喜欢

转载自blog.csdn.net/e1172090224/article/details/99415314