使用场景
一般情况下,当我们想表达‘这是将来要做的事情,要在当前的步骤完成以后发生。’这层意思的时候,通常会使用异步回调来搞定这种情况,这是很久以来的通用解决方案。
通常会写出一下代码:
[AppleScript] 纯文本查看 复制代码
| 1 |
|
通过callback来处理未来值,但是同时,无论你有没有意识到,你都默认的信任了第三方库,它会将结果提交给你,然后你是用值进行下一步的操作,但是这种信任通常是很脆弱的信任,可能在使用的过程中,碰到过一下的问题:
调用回调过早
调用回调过晚(或不被调用)
调用回调的次数过多
未能传递所需的环境和参数
吞掉可能出现的错误和异常
以上的痛点可能在开发中或多或少的碰到过,这种脆弱的回调函数调用信任不仅容易出现难以预料的 bug,可能在多异步流程控制方面,也会使你陷入回调函数地狱。
Promise
通常情况我们不希望仅仅传递回调函数给第三方,而是希望第三方给我们提供了解其任务何时结束的能力,然后由我们自己的代码来决定下一步做什么。
这种范式就称为 Promise
Promise 的出现,在一定程度上解决了上述问题,但是有些问题尚未完全解决。Promise 属于一种编程范式,它使异步编程变得结果可预测和获得强力的信任。
归结起来,Promise 有以下特点:
Promise 的决议结果可能是拒绝和完成;拒绝值和完成的Promise 是不一样的:完成值总是编程给出的,而拒绝值,通常称为拒绝原因。
Promise 决议后就是外部不可变的值,这就确保了在多处调用时值不会被有意无意地修改。
Promise是一种封装和组合未来值的异域复用的机制。
现在和未来的统一
[AppleScript] 纯文本查看 复制代码
| 1 2 |
|
在进行 + 运算时,默认 x 和 y 是已决议的值,也就是他们的值已经准备好。这是最常见的使用场景。现在设想一种情况,‘当 x 和 y 有一方没有决议时,就等待另一方决议,当两个值都是决议状态时,就进行求和计算’。针对这个场景,会有以下代码产生:
[AppleScript] 纯文本查看 复制代码
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 |
|
上例中,采用了回到函数的形式,来确定 x 和 y 是否决议,然后计算他们的结果,并传递出去。上述代码中,把 x 和 y 当做了未来值,但是在进行运算时,并不在意x 和 y 是否已经可用。换句话说, add 把现在和将来归一化了,因此可以确保这个 add 运算的输出是可以测的。
使用 Promise 来重写上面的代码
[AppleScript] 纯文本查看 复制代码
| 1 2 3 4 |
|
重写以后的代码看起来更加的简短,而且省去了很多条件判断,更清晰的表达出了想要 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 |
|
可以看到,第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。
当一个库的返回值可能是立即值 或 Promise 时。
当你可以确定一个返回值是 thenable 类型时。
当然,这个方法只是当你不能清晰的确定目标值是否是合法的 Promise 值时使用。
链接:https://juejin.im/post/5eb115a96fb9a043410a0968
1万+





