ES6的Promise

相信凡是写过javascript的童鞋也一定都写过回调方法(callback),简单说回调方法就是将一个方法func2作为参数传入另一个方法func1中,当func1执行到某一步或者满足某种条件的时候才执行传入的参数func2,例如下面的代码段

// 当参数a大于10且参数func2是一个方法时 执行func2
function func1(a, func2) {
    if (a > 10 && typeof func2 == 'function') {
        func2()
    }
}

func1(11, function() {
    console.log('this is a callback')
})

一般来说我们会碰到的回调嵌套都不会很多,一般就一到两级,但是某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱。极端情况如下图:

badcallback.jpeg

由此,Promise的概念就由社区提出并实现,作用与回调方法几乎一致,都是在某种情况下执行预先设定好的方法,但是使用它却能够让代码变得更简洁清晰

什么是Promise

Promise是异步编程的一种解决方案,它有三种状态,分别是pending-进行中resolved-已完成rejected-已失败

当Promise的状态又pending转变为resolved或rejected时,会执行相应的方法,并且状态一旦改变,就无法再次改变状态,这也是它名字promise-承诺的由来

ES6之前的Promise

在ES6中,Promise终于成为了原生对象,可以直接使用。但是在这之前,小伙伴们想要使用Promise,一般会借助于第三方库,或者当你知道其中的原理以后,也可以手动实现一个简易的Promise

当然,为了防止不可预知的bug,在生产项目中最好还是不要使用原生的或者自己编写的Promise(目前为止并不是所有浏览器都能很好的兼容ES6),而是使用已经较为成熟的有大量小伙伴使用的第三方Promise库,下面就为小伙伴推荐一个—— Bluebird

Promise的基本用法

声明一个Promise对象

// 方法1
let promise = new Promise ( (resolve, reject) => {
    if ( success ) {
        resolve(a) // pending ——> resolved 参数将传递给对应的回调方法
    } else {
        reject(err) // pending ——> rejectd
    }
} )

// 方法2
function promise () {
    return new Promise ( function (resolve, reject) {
        if ( success ) {
            resolve(a)
        } else {
            reject(err)
        }
    } )
}

注意:实例化的Promise对象会立即执行

Promise.prototype.then() VS Promise.prototype.catch()

.then()方法使Promise原型链上的方法,它包含两个参数方法,分别是已成功resolved的回调和已失败rejected的回调

promise.then(
    () => { console.log('this is success callback') },
    () => { console.log('this is fail callback') }
)

.catch()的作用是捕获Promise的错误,与then()的rejected回调作用几乎一致。但是由于Promise的抛错具有冒泡性质,能够不断传递,这样就能够在下一个catch()中统一处理这些错误。同时catch()也能够捕获then()中抛出的错误,所以建议不要使用then()的rejected回调,而是统一使用catch()来处理错误

promise.then(
    () => { console.log('this is success callback') }
).catch(
    (err) => { console.log(err) }
)

同样,catch()中也可以抛出错误,由于抛出的错误会在下一个catch中被捕获处理,因此可以再添加catch()

使用rejects()方法改变状态和抛出错误 throw new Error() 的作用是相同的

当状态已经改变为resolved后,即使抛出错误,也不会触发then()的错误回调或者catch()方法

then() 和 catch() 都会返回一个新的Promise对象,可以链式调用

promise.then(
    () => { console.log('this is success callback') }
).catch(
    (err) => { console.log(err) }
).then(
    ...
).catch(
    ...
)

Promise实例的异步方法和then()中返回promise有什么区别?

// p1异步方法中返回p2
let p1 = new Promise ( (resolve, reject) => {
    resolve(p2)
} )
let p2 = new Promise ( ... )

// then()中返回promise
let p3 = new Promise ( (resolve, reject) => {
    resolve()
} )
let p4 = new Promise ( ... )
p3.then(
    () => return p4
)

p1异步方法中返回p2

p1的状态取决于p2,如果p2为pending,p1将等待p2状态的改变,p2的状态一旦改变,p1将会立即执行自己对应的回调,即then()中的方法针对的依然是p1

then()中返回promise

由于then()本身就会返回一个新的promise,所以后一个then()针对的永远是一个新的promise,但是像上面代码中我们自己手动返回p4,那么我们就可以在返回的promise中再次通过 resolve() 和 reject() 来改变状态

Promise的其他api

Promise.resolve() / Promise.reject()

用来包装一个现有对象,将其转变为Promise对象,但Promise.resolve()会根据参数情况返回不同的Promise:

参数是Promise:原样返回 参数带有then方法:转换为Promise后立即执行then方法 参数不带then方法、不是对象或没有参数:返回resolved状态的Promise

Promise.reject()会直接返回rejected状态的Promise

Promise.all()

参数为Promise对象数组,如果有不是Promise的对象,将会先通过上面的Promise.resolve()方法转换

var promise = Promise.all( [p1, p2, p3] )
promise.then(
    ...
).catch(
    ...
)

当p1、p2、p3的状态都变成resolved时,promise才会变成resolved,并调用then()的已完成回调,但只要有一个变成rejected状态,promise就会立刻变成rejected状态

Promise.race()

var promise = Promise.race( [p1, p2, p3] )
promise.then(
    ...
).catch(
    ...
)

“竞速”方法,参数与Promise.all()相同,不同的是,参数中的p1、p2、p3只要有一个改变状态,promise就会立刻变成相同的状态并执行对于的回调

Promise.done() / Promise. finally()

Promise.done() 的用法类似 .then() ,可以提供resolved和rejected方法,也可以不提供任何参数,它的主要作用是在回调链的尾端捕捉前面没有被 .catch() 捕捉到的错误

Promise. finally() 接受一个方法作为参数,这个方法不管promise最终的状态是怎样,都一定会被执行

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏AzMark

Python 学习笔记之类与实例

类 (class) 封装一组相关数据,使之成为一个整体,并使用一种方法持续展示和维护。

281
来自专栏郭耀华‘s Blog

java匿名内部类

匿名类是不能有名称的类,所以没办法引用它们。必须在创建时,作为new语句的一部分来声明它们。这就要采用另一种形式的new语句,如下所示: new <...

3038
来自专栏Flutter入门

Kotlin中的函数

函数还可以用中缀表示法调用,当他们是成员函数或扩展函数,只有一个参数,用 infix关键字标注

734
来自专栏java工会

在Java中字符串是通过引用传递的?

1565
来自专栏LIN_ZONE

JavaScript经典作用域问题(转载)

注:本文转自 javascript经典面试题 全局变量和局部变量 变量作用域 如需转载,请注明出处:https://www.cnblogs.com/zhuch...

532
来自专栏专注 Java 基础分享

关于类的对象创建与初始化

今天,我们就来解决一个问题,一个类实例究竟要经过多少个步骤才能被创建出来,也就是下面这行代码的背后,JVM 做了哪些事情?

3996
来自专栏C/C++基础

野指针

指向非法的内存地址指针叫作野指针(Wild Pointer),也叫悬挂指针(Dangling Pointer),意为无法正常使用的指针。

761
来自专栏玩转JavaEE

MongoDB固定集合

一般情况下我们创建的集合是没有大小的,可以一直往里边添加文档,这种集合可以动态增长,MongoDB中还有一种集合叫做固定集合,这种集合的大小是固定的,我可以在创...

3197
来自专栏专注 Java 基础分享

关于类的对象创建与初始化

今天,我们就来解决一个问题,一个类实例究竟要经过多少个步骤才能被创建出来,也就是下面这行代码的背后,JVM 做了哪些事情? Object obj = new ...

3195
来自专栏java一日一条

Java字符串之性能优化

在程序中你可能时常会需要将别的类型转化成String,有时候可能是一些基础类型的值。在拼接字符串的时候,如果你有两个或者多个基础类型的值需要放到前面,你需要显式...

302

扫描关注云+社区