ES6整套异步解决方案

本文介绍了JavaScript中异步处理的发展历程,从依赖回调函数到Promise的规范化,再到ES7引入的async/await语法。通过示例展示了如何避免回调地狱,以及Promise和async/await的使用方法,最后讨论了异步任务的链式调用和处理方式。
摘要由CSDN通过智能技术生成

异步处理的演化

JS实现异步的代码模型主要依托于回调

dom.addEventListener("click", function(e){
  // 回调函数作为第二个参数传递,函数可接收一个参数
}, {})

dom.onclick = function(e){
  // 回调函数作为属性传递,函数可接收一个参数
}

fs.readFile("./txt", function(err, buffer){
  // 回调函数作为最后一个参数传递,函数可接收两个参数
})

这种设计实际上是有缺陷的

  1. 没有统一的标准

  2. 容易陷入回调地狱(callback hell)

    /*
    异步任务:依次发送7次网络请求,拿到服务器数据
    */
    asyncConnect("地址1", (resp1) => {
      // to do something
      asyncConnect("地址2", (resp2) => {
        // to do something
        asyncConnect("地址3", (resp3) => {
          // to do something
          asyncConnect("地址4", (resp4) => {
            // to do something
            asyncConnect("地址5", (resp5) => {
              // to do something
              asyncConnect("地址6", (resp6) => {
                // to do something
                asyncConnect("地址7", (resp7) => {
                  // to do something
                });
              });
            });
          });
        });
      });
    });
    

后来,JS社区提出了Promise A+规范,希望把异步规范化,并消除回调地狱

再后来,ES6 官方标准中提出了 Promise API 来处理异步,它满足Promise A+规范

由于异步处理变得标准了,就给ES官方提供了进一步改进的空间,于是在ES7中出现了新的语法async await,它更加完美的解决了异步处理问题

Promise的概念

一个promise就是一个对象,它表示一个异步任务

异步任务内部保存了它的进展状态,规范中约定有三种状态,不同的状态属于不同的阶段

状态的转换

任务开始时,始终处于未决阶段的挂起状态

任务在未决阶段的时候,有能力将其推向已决。比如,当从服务器拿到数据后,我们就从未决阶段推向已决的resolved状态,如果网络不好,导致出错了,我们就从未决阶段推向已决的rejected状态

我们把从未决推向已决的resolved状态的过程,叫做resolve从未决推向已决的rejected状态的过程,叫做reject

这种状态和阶段的变化是不可逆的,也就是说,一旦推向了已决,就无法重新改变状态

任务完成后附带的数据

任务从未决到已决时,可能附带一些数据,比如:跑步完成后的用时、网络请求后从服务器拿到的数据

任务的后续处理

任务已决后(有了结果),可能需要进一步做后续处理

针对resolved的后续处理,称之为thenable,针对rejected的后续处理,称之为catchable

Promise的基本使用

ES6 提供了一套API来适配上面提到的异步模型,这个API即Promise

var pro = new Promise((resolve, reject)=>{
    //未决阶段的代码,这些代码将立即同步执行,表示任务启动后要做的事情
    //...
    //在合适的时候,将任务推向已决
    //resolve(数据):将任务推向resovled状态,并附加一些数据
    //reject(数据):将任务推向rejected状态,并附加一些数据
})

注意

  1. 任务一旦进入已决后,所有企图改变任务状态的代码都将失效
  2. 以下代码可以让任务到达rejected状态
    1. 调用reject
    2. 代码执行报错
    3. 抛出错误

拿到Promise对象后,可以通过then方法指定后续处理

pro.then(thenable, catchable)
//或
pro.then(thenable)
pro.catch(catchable)

无论是thenable还是catchable,均是下面格式的函数

function (data){
    //data为状态数据
}

注意:后续处理函数一定是异步函数,并且放到微队列中

面试题

  1. 下面代码输出什么?

    const promise = new Promise((resolve, reject) => {
        console.log(1)
        resolve("a");
      	resolve("b");
      	reject("c")
        console.log(2)
    })
    promise.then(data => {
        console.log(data)
    }, resean => {
      	console.log(reason)
    })
    console.log(4)
    
  2. 下面的代码输出什么?

    setTimeout(() => {
      console.log(1);
    });
    var pro = new Promise((resolve, reject) => {
      console.log(2);
      resolve(3);
      console.log(4);
      throw 5;
      console.log(6);
    });
    
    console.log(7);
    
    pro.then(
      (data) => {
        console.log(data);
      },
      (reason) => {
        console.log(reason);
      }
    );
    

更多知识

Promise是可以链式调用的

var pro1 = ...; // pro1 是一个异步任务,它完成后会得到一个数字3

pro1
  .then(n=>{
  	console.log(n); // 输出:3
  	return n * 2;
	})
	.then(n=>{
  	console.log(n); // 输出:6
  	return n * 2;
	})
	.then(n=>{
  	console.log(n); // 输出:12
	})
/*
异步任务:依次发送7次网络请求,拿到服务器数据
*/
asyncConnect("地址1", (resp1) => {
  // to do something
  asyncConnect("地址2", (resp2) => {
    // to do something
    asyncConnect("地址3", (resp3) => {
      // to do something
      asyncConnect("地址4", (resp4) => {
        // to do something
        asyncConnect("地址5", (resp5) => {
          // to do something
          asyncConnect("地址6", (resp6) => {
            // to do something
            asyncConnect("地址7", (resp7) => {
              // to do something
            });
          });
        });
      });
    });
  });
});

/*
异步任务:依次发送7次网络请求,拿到服务器数据
*/
asyncConnect("地址1")
  .then((resp) => {
    // to do something
    return asyncConnect("地址2");
  })
  .then((resp) => {
    // to do something
    return asyncConnect("地址3");
  })
  .then((resp) => {
    // to do something
    return asyncConnect("地址4");
  })
  .then((resp) => {
    // to do something
    return asyncConnect("地址5");
  })
  .then((resp) => {
    // to do something
    return asyncConnect("地址6");
  })
  .then((resp) => {
    // to do something
    return asyncConnect("地址7");
  }).then((resp) => {
    // to do something
  });

如果使用ES7asyncawait,代码会更加优雅

/*
异步任务:依次发送7次网络请求,拿到服务器数据
*/
async function doRequest(){
  var resp1 = await asyncConnect("地址1");
  // to do something
  var resp2 = await asyncConnect("地址2");
  // to do something
  var resp3 = await asyncConnect("地址3");
  // to do something
  var resp4 = await asyncConnect("地址4");
  // to do something
  var resp5 = await asyncConnect("地址5");
  // to do something
  var resp6 = await asyncConnect("地址6");
  // to do something
  var resp7 = await asyncConnect("地址7");
  // to do something
}

面试题

  1. 下面的代码输出什么?

    var a;
    var b = new Promise((resolve, reject) => {
      console.log('promise1');
      resolve();
    }).then(() => {
      console.log('promise2');
    }).then(() => {
      console.log('promise3');
    }).then(() => {
      console.log('promise4');
    });
    
    a = new Promise(async (resolve, reject) => {
      console.log(a);
      await b;
      console.log(a);
      console.log('after1');
      await a
      resolve(true);
      console.log('after2');
    });
    
    console.log('end');
    
  2. 下面的代码输出什么?

    async function async1() {
        console.log('async1 start');
        await async2();
        console.log('async1 end');
    }
    async function async2() {
    	console.log('async2');
    }
    
    console.log('script start');
    
    setTimeout(function() {
        console.log('setTimeout');
    }, 0)
    
    async1();
    
    new Promise(function(resolve) {
        console.log('promise1');
        resolve();
    }).then(function() {
        console.log('promise2');
    });
    console.log('script end');
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值