Promise 总结

使用场景
一般情况下,当我们想表达‘这是将来要做的事情,要在当前的步骤完成以后发生。’这层意思的时候,通常会使用异步回调来搞定这种情况,这是很久以来的通用解决方案。
通常会写出一下代码:

[AppleScript] 纯文本查看 复制代码

?

1

ajax(url, callback)


通过callback来处理未来值,但是同时,无论你有没有意识到,你都默认的信任了第三方库,它会将结果提交给你,然后你是用值进行下一步的操作,但是这种信任通常是很脆弱的信任,可能在使用的过程中,碰到过一下的问题:

调用回调过早
调用回调过晚(或不被调用)
调用回调的次数过多
未能传递所需的环境和参数
吞掉可能出现的错误和异常

以上的痛点可能在开发中或多或少的碰到过,这种脆弱的回调函数调用信任不仅容易出现难以预料的 bug,可能在多异步流程控制方面,也会使你陷入回调函数地狱。
Promise

通常情况我们不希望仅仅传递回调函数给第三方,而是希望第三方给我们提供了解其任务何时结束的能力,然后由我们自己的代码来决定下一步做什么。
这种范式就称为 Promise

Promise 的出现,在一定程度上解决了上述问题,但是有些问题尚未完全解决。Promise 属于一种编程范式,它使异步编程变得结果可预测和获得强力的信任。
归结起来,Promise 有以下特点:

Promise 的决议结果可能是拒绝和完成;拒绝值和完成的Promise 是不一样的:完成值总是编程给出的,而拒绝值,通常称为拒绝原因。
Promise 决议后就是外部不可变的值,这就确保了在多处调用时值不会被有意无意地修改。
Promise是一种封装和组合未来值的异域复用的机制。

现在和未来的统一

[AppleScript] 纯文本查看 复制代码

?

1

2

var x,y = 2;

console.log(x+2) //NaN


在进行 + 运算时,默认 x 和 y 是已决议的值,也就是他们的值已经准备好。这是最常见的使用场景。现在设想一种情况,‘当 x 和 y 有一方没有决议时,就等待另一方决议,当两个值都是决议状态时,就进行求和计算’。针对这个场景,会有以下代码产生:

[AppleScript] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

function add(getX,getY,cb){

    var x,y;

    getX(function(val){

        x=val;

        if(y!==undefined) {

            cb(x+y)

        }

    })

    getY(function(val){

        y=val;

        if(x!==undefined) {

            cb(x+y)

        }

    })

}


上例中,采用了回到函数的形式,来确定 x 和 y 是否决议,然后计算他们的结果,并传递出去。上述代码中,把 x 和 y 当做了未来值,但是在进行运算时,并不在意x 和 y 是否已经可用。换句话说, add 把现在和将来归一化了,因此可以确保这个 add 运算的输出是可以测的。
使用 Promise 来重写上面的代码

[AppleScript] 纯文本查看 复制代码

?

1

2

3

4

function add(xPromise,yPromise) {

    Promise.all([xPromise,yPromise])

        .then( ([x,y]) => x+y )

}


重写以后的代码看起来更加的简短,而且省去了很多条件判断,更清晰的表达出了想要 x+y的和。同样在使用 Promise 时,即使需要的是未来值,同样不需要关心这个值是否已经决议,直接当做可使用的值只用就可以了。
Promise 代表的底层值的可用时间可能是现在或将来,但不管怎样,promise归一保证了行为的一致性。
关于 then
then 是使用 promise 时接收其结果的必须函数,也是链式调用的关键,then 有以下几个特点:

.then(...) 会创建一个promise,方便后续调用
.then(success,fail) 接受成功和失败回调,用来处理对应的结果。
.then(success,fail) 中的 fail 只能处理链式调用上一步 promise 产生的错误,而不能捕获处理自身和后面的错误。
使用.then(...) 时,建议添加一个错误处理函数,可以更好地捕获和处理错误。
上一步的 then 产生的错误,不会影响下一步的 then 的执行,看下面例子:
 

[AppleScript] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

//1

request('xxxxx')

//2

.then(res => {

    foo.bar() // 出错

    // 永远不会到达这里

    return request('xxxxx')

})

// 3

.then(res => {

    // 永远不会到达这里

}, err => {

    console.log(err);

    return 'err msg';

})

//4

.then( res => {

    console.log(res)   // 'err msg'

} )


可以看到,第2步出错以后,第3步的拒绝处理函数会步骤到这个错误。拒绝处理函数的返回值,如果有的话,会用来完成交给下一步骤(第4步)的promise,这样,这个链现在就回到了完成状态。
而且,上一步如果没有出错,上一步的任何返回值都会被下一步的成功处理函数接收到。
Promise.resolve()
Promise 在使用形式上,其实并没有完全摆脱回调。它们只是改变了传递回调的位置。并不是把回调函数传递给 foo(...),而是从 foo(...) 的到了某个东西,然后把回调传给了这个东西。
这时可能会产生疑问,为什么这就比单纯的使用回调更值得让人信任呢?如何确定返回值就是一个可信任的 Promise。这个问题有人是通过检测返回值是否是 promise 来解决,但是,如果这个值不是 promise,可信任的程度是不是就降低了。其实,ES6 的 promise 已经提供了对应的解决方案,就是 Promise.resolve()
 

[AppleScript] 纯文本查看 复制代码

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

Promise.resolve(...) 将接收的一个非 promise、非 thenable 的立即值,转换成一个立即用这个值填充的 promise。

Promise.resolve(...) 如果参数是一个 promise ,就会得到一个用这个值填充的 promise。

 

const p1 = Promise.resolve(42);

 

const p2 = Promise.resolve(p1);

 

p1 === p2 // true

 

// 这也就是说,如果向`Promise.resolve()`传递一个真正的`Promise`,就会返回同一个 `Promise`

 

Promise.resolve(...) 可以接受任何 thenable,将其解封为它的非 thenable 值。

 

// 考虑下面代码

const p = {

    then(cb,errcb) {

        cb(42);

        errcb('err msg')

    }

}

 

p.then( val=> {

    console.log(val) // 42

}, err => {

    // 这里不应该运行,但实际上却运行了

    console.log(err)// err msg

} )

 

// 上面的例子是个糟糕的体验,因为异常函数一定会被触发,这不符合 promise 的规范。

// 使用 Promise.resolve(...) 可以规范上述行为

Promise.resolve(p)

.then( val=> {

    console.log(val) // 42

}, err => {

    // 永远都走不到这里,除非发生错误

    console.log(err)

} )


鉴于 Promise.resolve(...) 的功能,可以在以下场景中使用:

当你不确定一个第三方库返回值是否是一个可信任的行为良好的 Promise。
当一个库的返回值可能是立即值 或 Promise 时。
当你可以确定一个返回值是 thenable 类型时。
当然,这个方法只是当你不能清晰的确定目标值是否是合法的 Promise 值时使用。

链接:https://juejin.im/post/5eb115a96fb9a043410a0968

06-30
<think>我们正在处理一个关于JavaScriptPromise的问题。用户希望了解Promise的基本概念和用法。根据提供的引用内容,我们可以总结如下: 引用[1]介绍了Promise是JavaScript中用于管理异步操作的对象,可以避免回调地狱。它有三种状态:pending(初始状态)、fulfilled(操作成功完成)和rejected(操作失败)。状态一旦改变就不可逆转。创建Promise的语法是使用构造函数,传入一个执行函数,该函数有两个参数:resolve(成功时调用)和reject(失败时调用)。 引用[2]同样说明了创建Promise对象的方式,并提到使用.then()处理成功和.catch()处理失败。 引用[3]给出了一个具体的例子:创建一个Promise对象,在setTimeout后调用resolve。根据这些信息,我们可以组织回答如下:1.介绍Promise的基本概念:是什么,为什么需要它(解决回调地狱,管理异步)。 2.解释Promise的三种状态。3.如何创建一个Promise对象。4.如何使用Promise(通过.then()和.catch()方法)。 5.可以给出一个简单的示例代码。 此外,我们还需要按照系统级指令的要求: -行内数学表达式使用$...$(但这个问题中可能没有数学表达式,所以可以忽略)。-独立公式单独成段(同样,可能不需要)。 -使用中文回答。 -生成相关问题。-在回答中引用的段落末尾添加引用标识。由于引用内容来自提供的三个引用,我们在回答中适当添加引用标识。 最后,生成3-5个相关问题。现在,我们开始编写回答。</think>### JavaScript Promise 概念与用法详解 #### 一、Promise 核心概念 Promise 是 JavaScript 中处理异步操作的标准方案,用于管理需要延迟执行的任务(如网络请求、文件读取等)。它通过状态机机制解决了传统回调函数的"回调地狱"问题,使异步代码更易读和维护[^1]。 **三大状态**: 1. **pending**:初始状态,异步操作未完成 2. **fulfilled**:操作成功完成(调用 `resolve` 触发) 3. **rejected**:操作失败(调用 `reject` 触发) 状态特性: $$ \text{状态转换} \quad \text{pending} \xrightarrow{\text{resolve}} \text{fulfilled} \quad \text{或} \quad \text{pending} \xrightarrow{\text{reject}} \text{rejected} $$ 状态一旦改变便不可逆,且每个 Promise 只有唯一终态[^1]。 #### 二、基本使用方法 ##### 1. 创建 Promise ```javascript const promise = new Promise((resolve, reject) => { // 异步操作(如 API 请求) if (/* 成功条件 */) { resolve('成功数据'); // 状态转为 fulfilled } else { reject('失败原因'); // 状态转为 rejected } }); ``` ##### 2. 处理结果 ```javascript promise .then(result => { console.log('成功:', result); // 处理 fulfilled 状态 }) .catch(error => { console.error('失败:', error); // 处理 rejected 状态 }) .finally(() => { console.log('操作结束'); // 无论成功失败都会执行 }); ``` #### 三、典型使用场景 1. **链式异步操作**(避免回调嵌套): ```javascript fetchData() .then(processData) .then(saveData) .catch(handleError); ``` 2. **并发控制**: ```javascript Promise.all([task1, task2, task3]) // 全部成功才继续 .then(results => /* 处理结果数组 */); Promise.race([task1, task2]) // 首个完成的任务 .then(firstResult => /*...*/); ``` 3. **定时操作**: ```javascript new Promise(resolve => setTimeout(resolve, 2000)) .then(() => console.log('2秒后执行')); ``` #### 四、关键特性 - **链式调用**:`.then()` 始终返回新 Promise,支持连续调用 - **错误穿透**:使用 `.catch()` 可捕获链中任意位置的错误 - **微任务机制**:Promise 回调属于微任务,优先于宏任务(如 `setTimeout`)执行 > 示例:模拟登录流程 ```javascript function login(user) { return new Promise((resolve, reject) => { setTimeout(() => { user === 'admin' ? resolve('登录成功') : reject('账号错误'); }, 1000); }); } login('admin') .then(msg => { console.log(msg); // 输出"登录成功" return '加载用户数据'; // 传递给下一个 then }) .then(data => console.log(data)) // 输出"加载用户数据" .catch(err => console.log(err)); // 捕获错误 ``` [^1]: Promise 的状态管理机制是其异步控制的核心基础 [^2]: 创建语法和执行流程是使用 Promise 的必要知识 [^3]: 链式调用模式显著提升异步代码可维护性
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值