javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > Promise实例方法then()、catch()、finally()

JavaScript Promise实例方法then()、catch()、finally()详细解析

作者:wuhen_n

Promise是ES6引入的解决异步编程的新解决方案,语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果,这篇文章主要介绍了JavaScript Promise实例方法then()、catch()、finally()的相关资料,需要的朋友可以参考下

适合有JavaScript基础,希望深入异步编程的开发者。我们将用最少的篇幅,讲透最核心的知识。

引言:Promise实例方法

在之前的文章中,我们探讨了Promise的状态机模型和不可变原则。理解了Promise的内在状态后,现在让我们转向它的外在行为——实例方法。如果说状态是Promise的"骨骼",那么实例方法就是它的"肌肉",赋予了Promise处理异步操作的能力。

我们可以把Promise就像一台自动咖啡机。状态(pendingfulfilledrejected)是它的指示灯,而then()catch()finally()方法就是它的操作按钮。只有了解每个按钮的确切功能和使用时机,我们才能制作出一杯完美的"异步咖啡"。

then()方法:链式调用的核心

then()的基本语法

Then()方法是为Promise对象添加处理程序的主要方法,也是Promise中最核心、最复杂的方法。它可以接受两个参数:onResolved处理函数onRejected处理函数 。这两个参数都是可选的,如果提供的话,则会在Promise进入 fulfilledrejected 状态时执行,如以下示例:

function onResolved(id) {
    console.log("resolved:" + id)
}

function onRejected(id) {
    console.log("rejected:" + id)
}

let p1 = new Promise((resolve, reject) => resolve())
let p2 = new Promise((resolve, reject) => reject())

p1.then(() => onResolved('p1'),
    () => onRejected('p1'))
p2.then(() => onResolved('p2'),
    () => onRejected('p2'))

上述代码中,由于Promise只能转换一次状态,所以p1和p2这两个操作一定是互斥的,其结果为:

resolved:p1
rejected:p2

由于onResolved处理函数 和 onRejected处理函数 这两个参数都是可选的,当我们只想提供 onRejected处理函数 时,可以在 onResolved 的位置上传入undefined或null,这样可以避免在内存中创建多余的对象,因此then()方法在实际使用中,其传参方式有三种:

promise.then(onFulfilled)  // 只处理成功
promise.then(null, onRejected)  // 只处理失败(不推荐)
promise.then(onFulfilled, onRejected)  // 同时处理成功和失败

注:

  • then()方法中,也可以传递一个非函数,如:promise.then(’Hello‘),但会被静默忽略,因此不推荐这种写法。
  • promise.then(null, onRejected)这种写法也是不推荐的,因为一般默认要处理成功回调。

then()的链式调用

Promise真正的威力来自于then()的链式调用能力,这不仅仅是语法糖,而是一种函数式编程范式的体现。我们来看看下面一个例子:

// 一个典型的链式调用示例
fetchUserData(userId)
  .then(validateUser)            // 1. 验证用户数据
  .then(enrichWithProfile)       // 2. 丰富个人信息
  .then(saveToDatabase)          // 3. 保存到数据库
  .then(notifySubscribers)       // 4. 通知订阅者
  .then(updateCache)             // 5. 更新缓存
  .then(finalizeOperation);      // 6. 完成操作

链式调用的四种返回值模式

返回普通值

当then()方法中返回一个普通值时,该值会被包装为一个新的Promise对象,如以下示例:

const myPromise = new Promise((resolve, reject) => {
    resolve('Hello');
})

myPromise.then(value => {
    console.log('收到:', value);
    return '新的普通值';  // 自动包装为 Promise.resolve('新的普通值')
}).then(newValue => {
    console.log('链式传递:', newValue);  // '新的普通值'
});

返回Promise

当then()方法中返回一个Promise对象时,会等待该Promise完成后,继续下一个then()方法的链式调用,如以下示例:

const myPromise = new Promise((resolve, reject) => {
    resolve('Hello')
})

myPromise.then(value => {
    return new Promise((resolve, reject) => {
        resolve(value + ' World')
    }) // 返回新的Promise
}).then(response => {
    // 等待new Promise完成
    console.log('处理后的结果:', response)  // Hello World
});

抛出异常

当then()方法中抛出异常时,会转换为一个rejected的Promise对象,如以下示例:

const myPromise = new Promise((resolve, reject) => {
    resolve('Hello')
})

myPromise.then(value => {
    if (!value.valid) {
        throw new Error('数据无效')  // 等价于 Promise.reject(new Error('数据无效'))
    }
    return processValue(value)
}).catch(error => {
    console.log('捕获到错误:', error.message)  // '数据无效'
})

没有返回或返回undefined

当then()方法中没有返回值,或返回值为undefined时,会将undefined作为一个值,继续传递,如以下示例:

const myPromise = new Promise((resolve, reject) => {
    resolve('Hello')
})

myPromise.then(value => {
    console.log('处理值:', value)
    // 没有return语句
}).then(nextValue => {
    console.log('下一个值:', nextValue)  // undefined
})

catch()方法:错误处理

catch()的本质

Catch() 用于给Promise添加拒绝处理程序,它只接受一个参数:onRejected处理函数。实际上,catch()方法是一个语法糖,相当于then(null, onRejected)的语法,它的出现极大地提高了代码的可读性。我们来看看下面一个例子:

// 以下两种写法完全等价
promise.catch(onRejected)
promise.then(null, onRejected)

// 但.catch()的可读性更好
fetchData()
  .then(process)
  .catch(handleError)  // 清晰:这里处理错误
  .then(continueAfterError)

// 对比:使用.then()的第二个参数
fetchData()
  .then(process, handleError)  // 不清晰:是处理fetch错误还是process错误?
  .then(continueAfterError)

错误传播机制

Promise的错误处理遵循冒泡原则:错误会沿着Promise链向后传递,直到被捕获,如以下示例:

// 错误传播示例
Promise.resolve()
    .then(() => {
        console.log('步骤1: 成功')
        return '第一步结果'
    })
    .then(result => {
        console.log('步骤2: 收到', result)
        throw new Error('步骤2发生错误')  // 抛出错误
    })
    .then(result => {
        console.log('步骤3: 这行不会执行')  // 被跳过
        return '第三步结果';
    })
    .catch(error => {
        console.log('捕获到错误:', error.message)  // '步骤2发生错误'
        return '从错误中恢复';
    })
    .then(recovery => {
        console.log('恢复后继续:', recovery)  // '从错误中恢复'
    });

finally()方法:资源清理

finally()方法的独特性

Finally()方法用于给Promise实例添加 onFinally处理程序,这个方法可以避免onResolved处理函数 和 onRejected处理函数 中出现冗余代码,无论Promise状态为 fulfilledrejected 的都会执行,即无论Promise是成功还是失败,它都会执行。我们来看看下面的例子:

// .finally()的基本使用
fetchData()
    .then(data => {
        console.log('数据处理:', data);
        return process(data);
    })
    .catch(error => {
        console.error('处理失败:', error);
        throw error; // 重新抛出,让外部知道失败
    })
    .finally(() => {
        console.log('清理资源');  // 无论成功失败都会执行
        cleanupResources();
    });

finally()方法的三个特性

不接受参数

promise.finally(() => {
    // 这里无法访问Promise的结果或错误原因
    console.log('执行清理')
})

原样传递上游的结果或错误

Promise.resolve('成功数据')
    .finally(() => {
        console.log('finally执行');
        // 这里返回的值不会影响链的传递
        return 'finally的返回值会被忽略';
    })
    .then(value => {
        console.log('收到:', value);  // '成功数据',不是'finally的返回值'
    })

如果finally抛出错误,会覆盖之前的错误

Promise.resolve('原始数据')
    .finally(() => {
        throw new Error('finally中的错误');  // 这个错误会覆盖之前的结果
    })
    .then(value => {
        console.log('这不会执行');  // 被跳过
    })
    .catch(error => {
        console.log('捕获的错误:', error.message);  // 'finally中的错误'
    });

总结

方法对比总结

方法主要用途返回值影响执行时机最佳实践
then()处理成功结果,链式传递决定下一环的输入前一个Promise完成后保持纯函数,明确返回值
catch()错误捕获和恢复可恢复错误或重新抛出链中任何错误发生时在适当层级处理,不要过早吞没错误
finally()资源清理和状态重置不影响结果传递无论如何都会执行只做清理,不返回业务数据

黄金法则

结语

本文主要介绍了Promise的三种实例化方法,对于文章中错误的地方或者有任何问题!

到此这篇关于JavaScript Promise实例方法then()、catch()、finally()的文章就介绍到这了,更多相关Promise实例方法then()、catch()、finally()内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文