ES6 新特性梳理系列丨Promise 对象

本文详细介绍了JavaScript的ES6新特性中Promise的概念和使用,包括其解决回调地狱问题的机制,三种状态转换,以及then、catch、all和race方法的用法。通过实例解析了Promise如何使异步代码更有序,更易于理解和维护。
摘要由CSDN通过智能技术生成

ES6 新特性梳理系列文章将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!


JavaScript 与 ECMAScript

JavaScript 诞生于1995年,设计者是就职于 Netscape 公司的工程师 Brendan Eich。它是一门仅用10天就完成设计的编程语言,但至今为止已对业界保持26年的影响,且势头愈发强劲

1996年 Netscape 将 JavaScript 提交给ECMA,希望它可以成为“标准化一个通用的、跨平台的、中立于厂商的脚本语言的语法和语义标准”,1997年 ECMA 确定将 JavaScript 作为浏览器脚本语言的标准,并为之重命名为 ECMAScript,所以通常来讲我们将 ECMAScript 视为 JavaScript 的标准,而 JavaScript 则是 ECMAScript 的实现及扩展

1997年-1999年连续发布了ES1-ES3发布,2000年开始酝酿新版本的升级内容,中间因标准委员会意见未能达成一致,只做了部分功能的小范围升级及支持,于2009年12月发布了过渡版 ECMAScript 5.0,2011年6月发布 ECMAScript 5.1 并成为 ISO 国际标准

2013年3月 ECMAScript 6 草案冻结,2013年12月 ECMAScript 草案发布,2015年6月 ECMAScript 6 正式通过,成为新的国际标准。ES6 泛指自2015年升级为 ECMAScript 6.0 后的所有子版本,如:ES2015-ES2020,等同于ES6.0-ES6.5,这是 JavaScript 走向企业级编程语言的强势升级

不断升级的标准与实现,对于开发效率及产品质量起到强有力的支撑,接下来我们开始梳理ES6的新特性吧!


为什么要有Promise

在 ES6 之前,我们处理异步操作大多使用的是回调函数形式,而回调函数形式存在两个致命弱点,一个就是我们熟知的回调地狱,当你的代码里出现了嵌套回调与链式回调的时候。

比如,你可以试着找出以下伪代码中,函数的执行顺序。先说明一下,

fn1(function(){
  // 这里是fn1的回调
})

现在,我们试着看下面代码

fn1(function(){
  fn2();
  fn3(function(){
    fn4()
   })
   fn5()
})
fn6()

尽管你是一个对回调有所了解的技术人员,也不能保证一眼看上去就可以说出fn1-6 的执行顺序。实际的执行顺序是:fn1 -> fn6 -> fn2 -> fn3 ->fn5 -> fn4 。由于回调函数的原因,我们如果需要在代码里,不停地移动视线去寻找真正的执行顺序。

另一个弱点是信任问题,我们的异步请求一般通过第三方工具来执行,比如熟知的ajax请求,并不是你自己编写的代码,也不在你的直接控制范围之内,你所定义的回调函数的执行次数和执行顺序完全由第三方工具决定,你还没有采取某种措施来解决回调函数造成的信任问题,那你的代码现在已经有了bug,即使它们还没有给你造成损害。隐藏的 bug 也是 bug。所以就有了我们的 promise。


什么是 Promise

Promise ,是对未来值的一种承诺,承诺过一段时间会给你一个结果。包含三种状态:pending(进行中)、resolved(已完成)和 rejected(已失败),并且状态的改变只会有两种情况,一个是从 pending 变为 resolved,一个是从pending 变成 rejected。当状态发生改变后,无论做任何操作,都无法改变这个状态了。

有了 promise ,可以使用我们的异步代码以同步的形式表达出来。


创建一个 Promise

var pro = new Promise((function(resolve, reject) {
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
})

上面代码中,通过 Promise 构造函数创建了一个 promise 实例,在构造函数中接收一个参数,参数形式是一个函数,函数的了两个参数也是函数,分别是resolve 和 reject。resolve() 函数的执行代表 promise 从 pending 状态变成了resolved 状态,也就是成功态,并且可以通过参数的形式将成功的信息传递出去(比如后台返回的数据);通过 reject(),可以将 promise 从 pending状态变成了 rejected 状态,也就是失败态,并且可以通过参数的形式将失败的信息传递出去(比如报错信息)。


Promise.then()

pro.then(function(value) {
  // 请求成功
}, function(error) {
  // 请求失败
});

对于创建的 promise 的实例来说,可以用 then 方法分别指定 resolved 状态和 reject 状态的回调函数。then 方法接收两个回调函数作为参数,第一个是promise 状态由 pending 变为 resolved 状态时的回调,第二个是变为 rejected 时的回调。其中第二个函数是可选的。

考虑如下代码执行情况

var pro = new Promise((function(resolve, reject) {
    console.log('1')
    resolve();
})
pro.then(function(){
  console.log('2')
})
console.log('3')


// 输出顺序
// 1 
// 3 
// 2

promise 实例在刚刚创建的时候就会执行,所以会先输出'1',然后 then 方法的回调函数会在同步代码执行之后才会执行,所以会先输出'3',然后是'2'。

then 方法返回的结果也是一个 promise 实例,但和原来的 promise 实例(也就是调用then的那个)并不是同一个,所以我们可以在 then 方法后面再调用另一个 then 方法,链式调用。

var pro = new Promise((resolve, reject) => {
    resolve('成功了')
});
pro.then(
    res => {
        console.log(res);
        return `${res} 第二次`;
    }
).then(
        res => console.log(res)
    );
    
// 成功了
// 成功了第二次

前一个回调函数返回的值,将会作为返回结果传递给第二个回调函数。这样是不是比先前的代码看起来更加有顺序性。以上代码中使用了箭头函数和字符串扩展形式,我们将在后期更新相关文章。


Promise.catch()

之前说过,then 方法的参数接收两个回调函数。当某个参数被省略的时候,那么一个默认的替代函数就会被采用,默认成功回调返回成功值,默认失败会返回失败原因值。

catch() 就是 then(null,function()) 的简写形式。用于指定发生错误时的回调函数,catch 回调函数返回的也是一个 promise 对象。

pro.then(function(){
  // 成功回调
}).catch(function(){
  // 失败回调
})
等同于
pro.then(funtion(){
  // 成功回调
}).then(null,function(){
  // 失败回调
});

Promise.all()

有时候我们的需求是两个异步操作都成功的情况下,才执行相关步骤。那么我们就可以使用 promise.all() 方法。接收多个 promise 实例,将它们包装成一个新的 promise 实例。all 方法接收一个参数,这个参数是一个可迭代数据结构(就是具有 iterator 接口),数组元素都是 promise 实例,如果不是,就会先调用 promise.resolve 方法将其转成 promise 实例。比如:

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

只有 p1、p2、p3 都成功的时候,p 才是成功状态,也就是 resolved 状态,否则就是 rejected 失败状态。当成功时, p1、p2、p3 的返回值组成一个数组,传递给 p 的回调函数,当失败时,第一个失败的 promise 实例的返回值,传递给 p 的回调函数。


Promise.race()

和 promise.all 相似,race 方法同样将多个 promise 实例封装成一个 promise实例。

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

不同的是,只要 p1、p2、p3 有一个的状态发生了改变(由 pending 变为了resolved 或者 rejected ),p 的状态就会发生改变,第一个发生改变的实例的返回值将传递给p的回调函数。


Promise.resolve()

上面提到过,我们可以通过 promise.resolve() 将非 promise 实例转为promise 实例,如果这个值已经是可以确定的promise实例,那么它的状态就会是 promise.resolve() 返回的 promise 实例的状态,如果不是,那么就会将其封装成一个 promise,状态为 resolved。

var pro = Promise.resolve(123)
等价于
var pro = new Promise(function(resolve,reject){
  resolve(123)
})
pro.then(function(val){
      console.log(val)  // 123
  }
))

Promise.reject()

和 Promise.resolve() 类似,只不过 reject() 返回的 promise 实例的状态是rejected。

var pro = Promise.reject(123)
等价于
var pro = new Promise(function(resolve,reject){
  reject(123)
})
pro.then(null,function(val){
      console.log(val)  // 123
  })
pro.catch(function(val){
      console.log(val)  // 123
  })

ES6 新特性梳理系列文章将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!

叶阳辉

HFun 前端攻城狮

往期精彩:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值