我是目录
- Promise
- new Promise(..) 构造器
- 如何确定promise的类型
- 如何信任Promise
- 链式流
- Promise的局限性
- async与await
- async
- await
- await 等到之后,做了一件什么事情?
异步编程的四种方式:
1,回调函数:先执行程序的主要逻辑,将耗时的操作推迟执行。简单、易理解,但不利于代码维护
2,事件监听:任务的执行不取决与代码的顺序,取决于事件是否发生。可绑定多个事件,每个事件可指定多个回调函数,有利于实现模块化,但流程会不清晰
3,发布/订阅(观察者模式):有一个信号中心,某个信号执行完成后,向信号中心publish一个信号,其他任务则向信号中心订阅这个信号,从而知道自己什么时候开始执行。可通过查看消息中心,了解存在多少信号及每个信号的订阅者,从而监控程序的运行
4,Promise:为异步编程提供统一接口,每个异步对象返回一个promise对象,该对象有一个then方法,允许指定回调函数。不必担心错过某个事件,回调函数一定会执行。
回调是编写和处理 JavaScript 程序异步逻辑的最常用方式,是这门语言中最基础的异步模式。
回调的写法不符合人脑的第一反应,需要思考一下后才能明白其执行顺序。还会引发回调地狱。
回调最大的问题是控制反转,它会导致信任链的完全断裂。回调时使用的函数(例如ajax)可能并不是你编写的函数,而是第三方的库,我们把这称为控制反转(inversion of control), 也就是把自己程序一部分的执行控制交给某个第三方。这时候就需要考虑这个回调可能发生的情况,例如调用回调过早或过晚,调用次数太多或太少,是否会吞掉错误和异常,等等。
Promise
new Promise(…) 构造器
构造器 Promise(..)
必须和 new
一起使用,并且必须提供一个函数回调。这个回调是同步的或立即调用的。这个函数接受两个函数回调,用以支持 promise 的决议。通常我们把这两个函数称为 resolve(..)
和 reject(..)
。
reject(..)
就是拒绝这个 promise;但 resolve(..)
既可能完成 promise,也可能拒绝,要根据传入参数而定。如果传给 resolve(..)
的是一个非 Promise、非 thenable
的立即值,这个 promise 就会用这个值完成。但是,如果传给 resolve(..)
的是一个真正的 Promise 或 thenable
值,这个值就会被递归展开,并且(要构造的) promise 将取用其最终决议值或状态。
Promise相当于一个监听事件:
function foo(x) {// 开始做点可能耗时的工作// 构造一个listener事件通知处理对象来返回return listener;
}
var evt = foo( 42 );
evt.on( "completion", function(){// 可以进行下一步了!
} );
evt.on( "failure", function(err){// 啊,foo(..)中出错了
} );
foo(..)
显式创建并返回了一个事件订阅对象,调用代码得到这个对象,并在其上注册了两个事件处理函数。
相对于面向回调的代码,这里的反转是显而易见的,而且这也是有意为之。这里没有把回调传给 foo(..)
,而是返回一个名为 evt 的事件注册对象,由它来接受回调。
一个很重要的好处是,可以把这个事件侦听对象提供给代码中多个独立的部分;在foo(..)
完成的时候,它们都可以独立地得到通知,以执行下一步:
var evt = foo( 42 );
// 让bar(..)侦听foo(..)的完成
bar( evt );
// 并且让baz(..)侦听foo(..)的完成
baz( evt );
对控制反转的恢复实现了更好的关注点分离,其中 bar(..)
和 baz(..)
不需要牵扯到foo(..)
的调用细节。类似地, foo(..)
不需要知道或关注 bar(..)
和 baz(..)
是否存在,或者是否在等待 foo(..)
的完成通知。
事件侦听对象 evt 就是 Promise 的一个模拟。
如何确定promise的类型
虽然使用new Promise
来创建,instanceof
却不能检测出Promise的类型。其中主要的原因是,Promise 值可能是从其他浏览器窗口( iframe 等)接收到的。这个浏览器窗口自己的 Promise 可能和当前窗口 /frame 的不同,或者其他库中并没有使用原生的Promise,而是自己封装的。因此这样的检查无法识别 Promise实例。
识别 Promise(或者行为类似于 Promise 的东西)就是定义某种称为 thenable
的东西,将其定义为任何具有 then(..)
方法的对象和函数。我们认为,任何这样的值就是Promise 一致的 thenable
。
if ( p !== null && ( typeof p === "object" || typeof p === "function" ) &&
typeof p.then === "function"
) {
// 假定这是一个thenable!
}
else {
// 不是thenable
}
但这有一定的风险,会把所有拥有then
方法的对象或函数都认成Promise。
如何信任Promise
Promise 并没有完全摆脱回调。它们只是改变了传递回调的位置。我们并不是把回调传递给 foo(..)
,而是从 foo(..)
得到某个东西(外观上看是一个真正的Promise),然后把回调传给这个东西。
如何能够确定返回的这个东西实际上就是一个可信任的 Promise 呢?
解决方案就是 Promise.resolve(..)
。
如果向 Promise.resolve(..)
传递一个非 Promise、非 thenable 的立即值,就会得到一个用这个值填充的 promise。