ECMAScript 6 语法学习——promise

JavaScript引擎是基于单线程事件循环的概念构建的,它采用任务队列的方式,将要执行的代码块放到队列中,当JavaScript引擎中的一段代码执行结束,事件循环会指定队列中的下一个任务来执行。事件循环是JavaScript引擎中的一段程序,负责监控代码执行并管理任务队列。
JavaScript执行异步调用的传统方式是事件的回调函数,但随着应用的复杂,事件和回调函数无法满足开发者想做的事情,因此,ECMAScript 6给出了promise这一更强大的异步编程解决方案。
一个promise可以通过promise构造函数来创建,这个构造函数值只接受一个参数:包含初始化promise代码的执行器(executor)函数,在该函数内包含需要异步执行的代码。执行器函数接受两个参数,分别是resolve函数和reject函数,这两个函数由JavaScript引擎提供,不需要我们自己编写。异步操作结束成功时调用resolve函数,失败时调用reject函数。

代码示例如下

const promise = new promise(function(resolve, reject) {
    // 开启异步操作
    setTimeout(function() {
        try {
            let c = 6 / 2
            // 执行成功调用resolve函数
            resolve(c)
        } catch(e) {
            // 执行失败调用reject函数
            reject(e)
        }
    }, 1000)
})

在执行器函数内部包含了异步调用,在1s后执行两个数的除法运算,如果成功,则用相除的结果作为参数调用resolve函数,失败则调用reject函数。
每个promise都会经历一个短暂的生命周期:先是处于进行中(pending)的状态,此时操作尚未完成,所以它也是未处理的(unsettled),一旦异步操作执行结束,promise则变为已处理的(settled)状态。操纵结束后,根据异步操作执行成功与否,可以进入以下两个状态之一:

  1. fulfilled:promise异步操作成功完成。
  2. rejected:由于程序错误或者其他一些原因,promise异步操作未能成功完成,即已失败。

一旦promise状态改变,就不会再变,任何时候都可以得到这个结果。promise对象的状态改变只有两种可能:

  • 从pending变为fulfilled
  • 从pending变为rejected

在promise状态改变后,我们怎么去根据不同的状态来做相应的处理呢?
promise对象有一个then()方法,它接受两个参数:

  • 第一个是当promise的状态变为fulfilled时要调用的函数,与异步操作相关的附加数据通过调用resolve函数传递给这个完成函数
  • 第二个是当promise的状态变为rejected时要调用的函数,所有与失败相关的附加数据通过调用reject函数传递给这个拒绝函数。

添加上述promise的then()方法的调用

promise.then(function(value) {
    // 完成, 输出3
    console.log(value)
}, function(err) {
    // 拒绝
    console.error(err.message)
})

then()方法的两个参数都是可选的。

只在执行失败后进行处理

// 给then()方法的第一个参数传递null
promise.then(null, function(err) {
    // 拒绝
    console.error(err.message)
})

promise对象还有一个catch()方法,用于在执行失败后进行处理,等价于上述then()方法。

使用catch()方法

promise.catch(function(err) {
    console.error(err.message)
})

通常是将then()方法和catch()方法一起使用来对异步操作的结果进行处理,这样能更清晰地指明操作结果是成功还是失败。

同时使用then()方法和catch()方法

promise.then(function(value) {
    // 完成
    console.log(value)
}).catch(function(err) {
    // 拒绝
    console.error(err.message)
})

使用箭头函数书写

promise.then(value => console.log(value))
    .catch(err => console.error(err.message))
  • 修改上述promise,将除数改为0,在Node中运行代码,结果为Infinity
  • 如果调用resolve函数或reject函数时带有参数,那么它们的参数会被传递给then()或catch()方法的回调函数
    promise支持方法链的调用形式,如上述代码所示,每次调用then()或者catch()方法 时实际上会创建并返回另一个promise,因此可以将promise串联调用。串联调用时,只有在前一个promise完成或被拒绝时,第二个才会被调用。

代码示例如下

const promise = new promise((resolve, reject) => {
    // 调用setTimeout模拟异步操作
    setTimeout(() => {
        let intArray = new Array(20)
        for (let i = 0; i < 20; i++) {
            intArray[i] = parseInt(Math.random() * 20, 10)
        }
        // 成功后调用resolve
        resolve(intArray)
    }, 1000)
    // 该代码会立即执行
    console.log("开始生成一个随机数的数组")
})

promise.then(value => {
    value.sort((a, b) => a-b)
    return value
}).then(value => console.log(value))

需要说明的是:

  1. promise的执行器函数内的代码会立即执行,因此无论setTimeout指定的回调函数执行成功与否,console.log()语句都会执行
  2. 在20个随机数生成完毕后,调用resolve(intArray),因而then()方法的完成处理函数被调用,对数组进行排序,之后返回value;接着下一个then()方法的完成处理函数开始调用,输出排序后的数组。
  3. promise链式调用时,有一个重要特性就是可以给后续的promise传递数据,只需要在完成处理函数中指定一个返回值(如上述代码中的return value)就可以沿着promise链继续传递数据。

在完成处理程序或拒绝处理程序中也可能会产生错误,使用promise链式调用可以很好地捕获这些错误。

代码示例如下

const promise = new promise((resolve, reject) => {
    resolve("Hello World")
})

promise.then((value) => {
    console.log(value)
    throw new Error("错误")
}).catch(err => console.error(err.message))

要注意的是,与JavaScript中的try/catch代码块不同,如果没有使用catch()方法指定错误处理的回调函数,那么promise对象抛出的错误不会传递到外层代码,即不会有任何反应。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容