08月17, 2017

Promise读书笔记(一)

Promise是什么?

打个比方:在M记点一个汉堡,收银员给了一张带有订单号的收据,这个收据就是Promise,它向我保证我在未来能够用这个收据换一个汉堡。在这个过程中我可以做别的事情。在Promise世界里,等待一段时间后,会有三种结果:

  • 服务员叫号,我拿到了汉堡。这叫做“完成”(fulfilled)
  • 服务员叫号,告诉我汉堡卖完了。这叫做“拒绝”(rejected)
  • 还没有叫号,可能永远都不会被叫到。这叫做“未决议”(pending)

如何检测某个值是否为真正的Promise

不能使用p instanceof Promise来检查。有两个原因:

  • Promise值可能从其他浏览器窗口(iframe等)接收。这个浏览器窗口的Promise可能和当前窗口的不同(为啥?)。因此这样无法识别Promise实例。
  • 库或框架自己实现Promise,而不是使用ES6 Promise。也无法识别。

于是,ES标准使用“鸭子类型”检测方式:

鸭子类型(duck typing): “如果它看起来像只鸭子,叫起来像只鸭子,那它一定就是只鸭子”。

简单来讲,就是检测函数或对象是否具有then( .. )方法(这种函数或方法被认为是 thenable 的)。

Promise如何解决信任问题

回调函数的调用方可能是同步,也可能是异步。这种不确定性会导致一系列信任问题:

  • 调用回调过早
  • 调用回调过晚(或不被调用)
  • 调用回调次数过少或过多
  • 未能传递所需的环境和参数
  • 吞掉可能出现的错误和异常

看看Promise能否解决这几个问题。

调用过早

就是一个任务有时同步完成,有时异步完成。

Promise中即使是立即完成的Promise(类似new Promise(function(resolve) { resolve(42); }))也无法被同步观察到。

即使一个Promise已经被决议,提供给then(..)的回调也总是被异步调用。

调用过晚

回调:

doA(function () {
    doC();
    doD(function () {
        doF();
    });
    doE();
});
doB();

如果doA和doD是异步的,调用顺序会是:A->B->C->D->E->F

但是假如doA和doD是同步的,顺序会是:A->C->D->F->E->B

而使用Promise:

p.then(function() {
    p.then(function() {
        console.log('C');
    });
    console.log('A');
});
p.then(function() {
    console.log('B');
});

顺序一定是:A->B->C

一个Promise决议后,该Promise上通过then(..)注册的回调都会在下一个异步时机节点上依次立即调用。而且互不影响。

回调未调用

首先,只要Promise决议,就会调用完成回调或者拒绝回调。

其次,如果Promise永远不被决议,可以使用race,加上超时处理。

Promise.race([
    foo(),
    timeoutPromise(3000)
]).then(function () {}, function(err) {
    // foo被拒绝,或没按时完成
})

调用次数过多或者过少

一个Promise只会决议一次。即resolve或reject方法调用一次,忽略之后的调用。

未能传递参数/环境值

Promise参数只有一个,如果没有显式决议,使用undefined。

JS函数总是保持其定义所在的作用域的闭包。不是Promise特有的优点。

吞掉错误或异常

如果拒绝一个Promise或者在出现JavaScript异常,就会在拒绝回调中被捕捉到。

如何保证Promise是真的Promise

使用Promise.resolve( .. )。可以规范异步任务,能避免有时候返回立即值,有时候返回Promise的情况。

  • 如果传递一个非Promise、非thenable的立即值,就会得到用该值填充的promise。
  • 如果传递一个真正的Promise,还是返回这个Promise。
  • 如果传递一个非Promise、thenable的值。会展开这个值,展开过程持续到一个具体的非类Promise的最终值。例如:
// 类Promise的对象
var p = {
    then: function(cb, errcb) {
        cb(42);
        errcb("muhaha")
    }
};
p.then(function (val) {
    console.log(val)
}, function (err) {
    // 啊,不应该运行!
    console.log(err)
})

而真正的Promise的决议结果要么是完成,要么是拒绝:

Promise.resolve(p).then(function (val) {
    console.log(val) // 42
}, function(err) {
    // 永远不会到这里
    console.log(err)
})

术语记录

revealing constructor: new Promise( function(..){ .. } )模式。特点:传入的参数会立即执行。

本文链接:http://imhxl.com/post/promise-note-1.html

-- EOF --

Comments