Promise详解-1:初识Promise

最近在回顾ES6的知识,想整理下跟Promise相关的内容。我准备整一个Promise解读的系列,看看能深入到什么程度吧。由浅入深,先认识下Promise。

痛苦的回忆:回调地狱

假如现在让你维护一个“古老”的项目,缺少脚手架的加持,只能使用es5的语法来开发,我相信你一定会深刻的体会到es6添加Promise是多么伟大的行为。在es5的时代,异步编程需要依赖回调函数来实现,这就会导致一个问题:回调地狱(callback hell),代码结构会变成复杂,可读性、可维护性差到极致。例如:如果有一个业务需要连续调用三个接口,那么就需要这么写:

ajax('api1', function(res1){
  if(res1.success) {
    ajax('api2', function(res2) {
      if (res2.success) {
        ajax('api3', function(res3){
          if(res3.success) {
            // dosomething....
          }
        });
      }
    })
  }
})

如果场景更复杂的话,可能就会变成这样:

除了接口调用,事件、异步加载等等需要等待的操作基本都需要通过回调来实现。整个项目中充斥着这样的代码基本就只能靠记忆去维护。

Promise是什么?

es6的规范中新增Promise对象,是异步编程的一个解决方案。Promise相当于提供一个容器,这个容器会封装一个单独的“时间线”,这个独立的“时间线”与主线程的执行是并行,当容器内发生变化的时候,容器会更新其状态,外部可以通过API获取到状态变更的结果,并且这个状态会被固定在容器中,保持不变,无论什么时候都可以获取。

Promise对象提供了更清晰的异步操作处理,可以将异步操作封装到Promise内,通过统一的方法来处理异步操作。当包装在Promise中的异步操作有结果时,Promise对象会根据异步操作的结果(正确或是错误)更新状态,并返回结果,我们可以通过Promise的then或者catch方法来获取异步操作的结果。针对上面的多个异步串联的操作,Promise对象提供链式调用的方式,通过then方法将多个异步操作串联起来,形成一个操作序列。这种方式可以避免回调地狱,将多层嵌套变成使得代码更加清晰和易于理解。针对其他多个异步操作的场景,Promise对象还提供了一些静态方法,如Promise.all和Promise.race,用于处理多个异步操作的结果。这些功能使得Promise对象成为处理异步操作的强大工具,能够大大简化异步操作的处理流程,提高代码的可维护性和可读性。

Promise的状态

Promise通过状态来定义包裹的异步操作的目前所处的阶段,Promise提供了三个互斥的状态:fulfilled(已成功) 、rejected(已失败)、pending(进行中),任何Promise对象都处于这三种状态之一。

当我们创建一个Promise对象时,这个Promise对象处于pending状态,再异步操作产生结果后,会有以下两种状态的变化:

  • 异步操作成功:pending转换到fulfilled
  • 异步操作失败:pending转换到rejected

这个状态一旦变更,就不会再改变,而且无论是什么时候都可以获取到对应的结果。就是说在Promise对象的状态发生变化后,在任何时候都可以为Promise添加回调函数都可以会立即得到这个结果。这与事件是完全不同,在事件的模式下,如果你错过了它,就无法在获取结果。

Promise的基础用法

按照ES6的规范,Promise对象内置的Promise构造函数来实例化:

let promise = new Promise(function(resolve, reject) {
    // do sync 
    resolve(); // 将promise的状态置为fulfilled(已成功)
    reject(); // 将promise的状态置为rejected(已失败)
});

这时,我们就得到一个`Promise`对象的实例:`promise`,然后通过这个实例,可以在任何时候取得异步操作的结果:

promise.then(() => {
  // do something
}).catch((error) => {
  console.error(error);
});

通过thencatch两个方法分别捕获Promise对象中包裹的异步操作的结果和错误。

这里需要注意的是,异步操作结束后会通过resolvereject来修改实例的状态。那么这里就不一定是真正反应异步操作的实际结果,而是异步操作想要外界知道的结果。这样说可能有点绕,举个例子,在请求一个接口时,请求是一个异步动作,按照ajax工具思路,应该是根据状态码来修改状态。但是在实际操作中,接口返回200,但是内容是操作失败,虽然这里的接口是请求成功(就http请求而言),但是在业务上是失败的。一般这里也会直接处理成失败的状态而不是成功状态(这就是异步操作真正想要给外界的结果)。

先整个例子

上面是Promise的基础用法,为了更好的说明,这里我先整个例子:现在假设在某个业务系统中,所有接口的请求的返回格式都是:

{
  "errCode": 0,			// 0 成功 1 失败
	"data": [],				// 数据
  "message": ''     // 信息
}

我们通过Promise + XmlHttpRequest简单封装一个ajax方法:

function createUrlParams(data) {
  return Object.keys(data).map((v) => {
    return `${v}=${data[v]}`;
  }).join('&');
}

function ajax(url = '',  data = {}, type = 'GET') {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        
        type = type.toUpperCase();

        if (type === 'GET') {
            url += '?' + createUrlParams(data);
            data = null;
        }

        xhr.open(type, url, true);
        
        xhr.onload = function() {
            if (xhr.status >= 200 && xhr.status < 300) {
                let res = JSON.parse(xhr.response);
                if (res.errCode !== 0) {
                    reject({status:'error', result: res});
                } else {
                    resolve(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值