archerU / notes

博客笔记 预计写七个系列:JavaScript深入系列、JavaScript专题系列、数据结构和算法系列、设计模式系列、React系列、Webpack 系列、前端开发日常优化方案

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

JavaScript深入系列之Promise

archerU opened this issue · comments

异步编程中通过回调表达程序异步和管理并发的两个主要缺陷:缺乏顺序性和可信任性。

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 决议为拒绝,它就会拒绝。