[笔记] Javascript 中的 Promise

如有梯子, 请看原文
Promise API 参考

除非额外注明,Chrome、Opera 和 Firefox(nightly)均支持下列所有方法。这个 polyfill 则在所有浏览器内实现了同样的接口。

静态方法

Promise.resolve(promise);
返回一个 Promise(当且仅当 promise.constructor == Promise)
Promise.resolve(thenable);
从 thenable 对象创建一个新的 Promise。一个 thenable(类 Promise)对象是一个带有“then”方法的对象。
Promise.resolve(obj);
创建一个以 obj 为肯定结果的 Promise。
Promise.reject(obj);
创建一个以 obj 为否定结果的 Promise。为了一致性和调试便利(如堆栈追踪),obj 应该是一个 Error 实例对象。
Promise.all(array);
创建一个 Promise,当且仅当传入数组中的所有 Promise 都肯定之后才肯定,如果遇到数组中的任何一个 Promise 以否定结束,则抛出否定结果。每个数组元素都会首先经过 Promise.resolve,所以数组可以包含类 Promise 对象或者其他对象。肯定结果是一个数组,包含传入数组中每个 Promise 的肯定结果(且保持顺序);否定结果是传入数组中第一个遇到的否定结果。
Promise.race(array);
创建一个 Promise,当数组中的任意对象肯定时将其结果作为肯定结束,或者当数组中任意对象否定时将其结果作为否定结束。
备注:我不大确定这个接口是否有用,我更倾向于一个 Promise.all 的对立方法,仅当所有数组元素全部给出否定的时候才抛出否定结果

构造器

new Promise(function(resolve, reject) {});
resolve(thenable)
你的 Promise 将会根据这个 “thenable” 对象的结果而返回肯定/否定结果
resolve(obj)
你的 Promise 将会以 obj 作为肯定结果完成
reject(obj)
你的 Promise 将会以 obj 作为否定结果完成。出于一致性和调试(如栈追踪)方便,obj 应该是一个 Error 对象的实例。构造器的回调函数中抛出的错误会被立即传递给 reject()。
实例方法

promise.then(onFulfilled, onRejected)
当 promise 以肯定结束时会调用 onFulfilled。 当 promise 以否定结束时会调用 onRejected。 这两个参数都是可选的,当任意一个未定义时,对它的调用会跳转到 then 链的下一个 onFulfilled/onRejected 上。 这两个回调函数均只接受一个参数,肯定结果或者否定原因。 当 Promise.resolve 肯定结束之后,then 会返回一个新的 Promise,这个 Promise 相当于你从 onFulfilled/onRejected 中返回的值。如果回调中抛出任何错误,返回的 Promise 也会以此错误作为否定结果结束。
promise.catch(onRejected)
promise.then(undefined, onRejected) 的语法糖。

Java thread 线程的6种状态及转换

Java 线程共有6种状态:

  1. NEW 一个线程刚创建, 尚未开始运行. 这种状态不会出现在 thread dump 种. 比如代码中 new 了一个 Thread(), 还没调用 start() 方法, 就是 NEW 这个状态.
  2. RUNNABLE 线程正在运行或可运行状态,但是被挂起. 线程运行都是基于时间片的, 只要线程属于可运行状态(RUNNABLE), 就会给它分配时间片, 一旦时间片分配到该线程, 它就立马能运行.
  3. BLOCKED 线程正在等待一个 monitor 锁. 它处于该锁的 block 队列里面. 这个完全是跟 java 的 synchronize 关键字有关, 在线程等待进入 synchronize 里面的同步区之前, 尚未获得锁的时候, 就处于 BLOCKED 状态. 也只有这种情况, 线程才能进入 BLOCKED 状态;
  4. WAITING 线程正在等待其它线程触发某种操作, 在其它线程没有触发某种操作之前, 它只能在这个 WAITING 状态等待;
  5. TIMED_WAITING 同上面的 WAITING 状态, 只不过它有最长等待时间, 一旦时间到, 它就退出这种 TIMED_WAITING 状态;
  6. TERMINATED 线程以及结束. 这种状态也不可能出现在 thread dump 当中.

其中 NEW 和 TERMINATED 都很容易理解, 一个是开始之前, 一个是结束之后, 都不会出现在 thread dump 中. RUNNABLE 表示线程处在正在运行或随时可运行状态, 只要给它分配到时间片, 它立马就能运行. 所以 RUNNABLE 状态并不代表它正在运行.

只有当一个线程想要进入 Synchronize 保护的代码区时候, 才有可能进入 BLOCKED 状态. 如果它获得了 monitor 锁, 那么它直接进入被保护的代码块, 还是 RUNNABLE 状态. 如果没获得,那么进入该 monitor 的 block 队列(Entry Set), 线程则变成 BLOCKED 状态. 另外一个很容易忽略的地方是,当一个线程曾经拥有 monitor 锁, 然后因为某种原因需要挂起(已经进入被 synchronize 保护的代码区, 由于某种条件不满足, 不能退出被保护的代码区, 只能挂起当前线程), 这时该线程调用 Object.wait() 方法, 线程变成 WAITING 状态, 这时线程进入该 monitor 的 wait 队列 (Wait Set), 如果有其它线程调用该对象的 notify/notifyAll 方法, 那么这些线程被从 wait 队列 移入 block 队列( Entry Set), 这时该线程又要重新竞争 monitor 锁, 这时线程状态变化为 BLOCKED 状态. 所以不论是直接竞争锁未得到锁, 还是因为之前由于 wait 进入 Wait Set, 后来被 notify 之后, 重新进入 Entry Set 等待竞争monitor 锁, 只有这2种情况, 线程会进入 BLOCKED 状态.

对于 WAITING 状态, 只有下面这几种情况, 线程才可能进入 WAITING 状态:

1. Object.wait with no timeout
2. Thread.join with no timeout
3. LockSupport.park 

通过 Object.wait 进入的线程, 进入该 monitor 锁的 Wait Set, 等待该 Object 的 notify/notifyAll 事件;

通过 Thread.join 进入的线程, 等待另外一个线程的结束. 实际的实现还是通过 synchronize 的 monitor 锁和 wait 方法实现的. 假如有线程 threadA 和 threadB 两个线程, threadA 通过调用 threadB.join() 方法等待 threadB 的结束. 实际实现是 threadB 对象的 join 方法是 synchronized, 它先是获得了 threadB 对象的 monitor 锁, 然后又进入了该 monitor 的 wait() 方法, 进入 Wait Set, 进入等待状态. 当 threadB 结束运行之前, 它会调用 threadB 对象的 notifyAll() 方法, 那么所有在 Wait Set 的线程进入 Entry Set, 之后竞争 threadB 对象的 monitor 锁, 然后释放锁, 继续执行, 最终通过这种方式实现对其它线程的终止状态的等待.

通过 LockSupport.park 进入的线程, 一般都是通过 AQS 进入的, 它们都在等待 LockSupport.unpark() 方法.

对于 TIMED_WAITING 状态, 下面这几种情况会进入该状态:

1. Thread.sleep
2. Object.wait with timeout
3. Thread.join with timeout
4. LockSupport.parkNanos
5. LockSupport.parkUntil