异步编程中通过回调表达程序异步和管理并发的两个主要缺陷:缺乏顺序性和可信任性。
Promise
Promise 是一种封装和组合未来值的易于复用的机制,用于解决异步编程中可信任性问题。
顺序的大脑
// A
setTimeout(function() {
// C
},1000);
// B
// 交换x和y,通过临时变量z
z = x;
x = y;
y = z;
思考以上两个伪代码片段的执行顺序。第一个我们大脑这么描述:执行 A,设定延时 1000ms,然后执行 B,然后 1000ms 后执行 C。
第二个我们大脑这么描述: z=x 执行完毕后,然后执行 x=y,最后执行 y=z。第二个描述,我们的大脑思考方式是一步一步的,但是第一个不是。
回调
缺乏顺序性,回调函数会导致代码变得更加难以理解(跳来跳去查看流程)、追踪(跟踪异步流困难)、调试和维护。
doA(function() {
doB();
doC(function() {
doD();
});
doE();
});
doF();
如果 doA,doC 是异步执行的,伪代码的执行顺序是这样的:
- doA()
- doF()
- doB()
- doC()
- doE()
- doD()
如果 doA,doC 是同步执行的,伪代码的执行顺序是这样的:
- doA()
- doB()
- doC()
- doD()
- doE()
- doF()
回调导致我们跟踪异步流非常困难。同步或异步行为引起的不确定性导致bug追踪困难。并且嵌套在异步流中的代码通常无法复用。一旦控制流程变化,回调中的代码也会导致很难以维护和更新。
信任
回调最大的问题是控制反转导致的信任问题。因为回调暗中把控制权交给第三方(通常是不受你控制的第三方工具!)来调用你代码中的 continuation 。这种控制转移导致一系列麻烦的信任问题。
// A
ajax("..",function(..) {
// C
});
// B
这样的代码通常不会有问题。如果 ajax(..) 不是你编写的代码,而是第三方的提供的工具,那就相当于把自己程序一部分的执行控制权交给了第三方,这就会导致一系列信任问题。
Promise 信任问题
调用回调过早
promise 提供给 then(..) 的回调总是会被异步调用。
调用回调过晚
promise 决议后,所有通过 then(..) 注册的回调都会在下一个异步时机点上依次被立即调用。
回调未调用
promise 在决议时总是会调用一个完成回调或一个拒绝回调。
调用回调次数过少或过多
promise 只能被决议一次。
未能传递参数/环境值
promise 至多只能有一个决议值(完成或拒绝)。如果没有任何值显示决议,那么这个值就是 undefined。
吞掉错误或异常
promise 会把出错消息传给拒绝回调。
Promise 模式
Pormise.all([..])
promise.all([..]) 返回的主 promise 在且仅在所有的成员 promise 都完成后才会完成。如果这些 promise 中有任何一个被拒绝的话,主 Promise.all([..]) promise 就会立即被拒绝,并丢弃来自其他所有 promise 的全部结果。
Promise.race([..])
promise.race([..]) 一旦有任何一个 Promise 决议为完成,Promise.race([..]) 就会完成,一旦有任何一个 Promise 决议为拒绝,它就会拒绝。
异步编程中通过回调表达程序异步和管理并发的两个主要缺陷:缺乏顺序性和可信任性。
Promise
Promise 是一种封装和组合未来值的易于复用的机制,用于解决异步编程中可信任性问题。
顺序的大脑
思考以上两个伪代码片段的执行顺序。第一个我们大脑这么描述:执行 A,设定延时 1000ms,然后执行 B,然后 1000ms 后执行 C。
第二个我们大脑这么描述: z=x 执行完毕后,然后执行 x=y,最后执行 y=z。第二个描述,我们的大脑思考方式是一步一步的,但是第一个不是。
回调
缺乏顺序性,回调函数会导致代码变得更加难以理解(跳来跳去查看流程)、追踪(跟踪异步流困难)、调试和维护。
如果 doA,doC 是异步执行的,伪代码的执行顺序是这样的:
如果 doA,doC 是同步执行的,伪代码的执行顺序是这样的:
回调导致我们跟踪异步流非常困难。同步或异步行为引起的不确定性导致bug追踪困难。并且嵌套在异步流中的代码通常无法复用。一旦控制流程变化,回调中的代码也会导致很难以维护和更新。
信任
回调最大的问题是控制反转导致的信任问题。因为回调暗中把控制权交给第三方(通常是不受你控制的第三方工具!)来调用你代码中的 continuation 。这种控制转移导致一系列麻烦的信任问题。
这样的代码通常不会有问题。如果 ajax(..) 不是你编写的代码,而是第三方的提供的工具,那就相当于把自己程序一部分的执行控制权交给了第三方,这就会导致一系列信任问题。
Promise 信任问题
调用回调过早
promise 提供给 then(..) 的回调总是会被异步调用。
调用回调过晚
promise 决议后,所有通过 then(..) 注册的回调都会在下一个异步时机点上依次被立即调用。
回调未调用
promise 在决议时总是会调用一个完成回调或一个拒绝回调。
调用回调次数过少或过多
promise 只能被决议一次。
未能传递参数/环境值
promise 至多只能有一个决议值(完成或拒绝)。如果没有任何值显示决议,那么这个值就是 undefined。
吞掉错误或异常
promise 会把出错消息传给拒绝回调。
Promise 模式
Pormise.all([..])
promise.all([..]) 返回的主 promise 在且仅在所有的成员 promise 都完成后才会完成。如果这些 promise 中有任何一个被拒绝的话,主 Promise.all([..]) promise 就会立即被拒绝,并丢弃来自其他所有 promise 的全部结果。
Promise.race([..])
promise.race([..]) 一旦有任何一个 Promise 决议为完成,Promise.race([..]) 就会完成,一旦有任何一个 Promise 决议为拒绝,它就会拒绝。