Promise 总结
异步与并行常常被混为一谈。记住,异步是关于现在和将来的时间间隙,而并行是关于能够同时发生的事情。
javascript 是基于事件循环(eventLoop)来处理异步的,那么什么是事件循环?先看一段伪代码
var eventLoop=[];
var event;
while(true){
// 一次tick
if(eventLoop.length > 0){
// 拿到队列中的下一个事件
event = eventLoop.shift();
// 现在,执行下一个事件
try{
event();
}catch(err) {
reportError(err);
}
}
}循环的每一轮称为一个tick,每一次tick会从队列中取出一个事件并执行,这些事件就是回调函数,对于setTimeout(..)并没有立即把回调函数放入时间循环队列中,而是等定时器到时后把回调函数放到eventLoop队列的队尾,如果队列中已存在一些事件,回调函数就会等待,所以setTimeout(..)定时器的精度可能不高。
任务
在ES6中,有一个新的概念建立在事件循环队列之上,叫做任务队列(job queue),它是挂在事件循环队列的每一个tick之后的一个队列。就好比在银行排队办理业务,轮到你办理业务的时候你突然想起来还有一个事要办,这个时候你会告知柜台还有件事要办而不是再次去排队,这个事就是一个任务,是插入到一次tick的后面而不是插入到事件队列的队尾。
一个任务可能引起更多任务被添加到同一个队列末尾所以,理论上说,任务循环(job loop)可能无限循环(一个任务总是添加另一个任务,以此类推),进而导致无法转移到下一个事件循环 tick。
关于事件循环与任务,可以参考另一片文章事件循环与任务队列
承诺 (Promise)
类型检查(typecheck)一般用术语鸭子类型(duck typing)来表示——“如果它看起来像只鸭子,叫起来像只鸭子,那它一定就是只鸭子”。于是识别Promise类似下面这种方式
使用promise来规避回调金字塔,ajax请求方法返回一个Promise对象
Promise并行与竟态
并行执行异步任务
设置超时,Promise也提供了解决方案,其使用了一种称为竞态的高级抽象机制:
是可信任的Promise吗?
如果向 Promise.resolve(..) 传递一个非 Promise、非 thenable 的立即值,就会得到一个用 这个值填充的 promise。下面这种情况下,promise p1 和 promise p2 的行为是完全一样的:
而如果向 Promise.resolve(..) 传递一个真正的 Promise,就只会返回同一个 promise:
如果向 Promise.resolve(..) 传递了一个非 Promise 的 thenable 值,前者就会 试图展开这个值,而且展开过程会持续到提取出一个具体的非类 Promise 的最终值。
Promise.resolve(..) 可以接受任何 thenable,将其解封为它的非 thenable 值。从 Promise. resolve(..) 得到的是一个真正的 Promise,是一个可以信任的值。如果你传入的已经是真 正的 Promise,那么你得到的就是它本身,所以通过 Promise.resolve(..) 过滤来获得可信 任性完全没有坏处。
从完成(或拒绝)处理函数返回 thenable 或者 Promise 的时候也会发生同样的展开。考虑:
虽然我们把 42 封装到了返回的 promise 中,但它仍然会被展开并最终成为链接的 promise 的决议,因此第二个 then(..) 得到的仍然是 42。如果我们向封装的 promise 引入异步,一 切都仍然会同样工作:
使链式流程控制可行的 Promise 固有特性:
调用 Promise 的 then(..) 会自动创建一个新的 Promise 从调用返回
在完成或拒绝处理函数内部,如果返回一个值或抛出一个异常,新返回的(可链接的)Promise 就相应地决议。
如果完成或拒绝处理函数返回一个 Promise,它将会被展开,这样一来,不管它的决议值是什么,都会成为当前 then(..) 返回的链接 Promise 的决议值。
术语:决议、完成以及拒绝
对于术语决议(resolve)、完成(fulfill)和拒绝(reject)
现在我们来解释为什么单词 resolve(比如在 Promise.resolve(..) 中)如果用于表达结果 可能是完成也可能是拒绝的话,既没有歧义,而且也确实更精确:
Promise.resolve(..) 会将传入的真正 Promise 直接返回,对传入的 thenable 则会展开。如果这个 thenable 展开得到一个拒绝状态,那么从 Promise.resolve(..) 返回的 Promise 实际上就是这同一个拒绝状态。所以对这个 API 方法来说Promise.resolve(..) 是一个精确的好名字,因为它实际上的结果可能是完成或拒绝。
Last updated
Was this helpful?