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