第11章:期约和异步函数
Promise(期约)
基本概念:
es6正式新增了Promise引用类型,支持定义和组织异步逻辑,接下来使用async和await关键字定义异步函数的机制
为什么要异步,是为了优化因计算量大而时间长的操作,如果在等待其他操作完成的同时,系统也能保持稳定的运行其他指令,最重要的是异步操作并不会等很长时间,所以只要你不想为等待某个堵塞线程执行,任何时候都可使用
同步和异步的区别:
- 同步对应内存的孙旭执行的处理器指令,每条指令都会按照他们出现的顺序来执行,执行流程容易分析程序在执行到代码任意位子的状态(比如变量的值)
- 异步操作,类似于系统中断,当前进程的外部的实体可以出发代码执行,异步操作是必要的,因为强制进程等待一个操作通常会造成大量时间的浪费,如果代码要访问一个高延迟的资源,就会出现长时间等待
Promise 可以通过new操作符来实例化,创建新期约需要传入执行器函数作为参数,
let p = new Promise(() => {});
setTimeout(console.log, 0, p); // Promise <pending>
小结:这个例子实例化了一个promise,然后打印他的时候显示状态为pending这里就引出了期约的三种状态机,
期约状态机
- 期约是一个有状态的对象,共有3种状态
- 待定(pending):是期约最初始的状态,在这个状态,期约可以确定一个代表成功**(兑现)「的状态,或者一个代表失败」(拒绝)**的状态,无论落定哪种状态都是不可逆的,一旦落定不再改变,而且不能保证必然会离开待定状态。
- 兑现:兑现状态是代表成功的,
- 拒绝:代表状态是拒绝的
小结:这三种状态是期约可以提供的最有用的信息,比如期约要向服务器发送一个http,请求返回200-299的状态就让期约「兑现」,否则就让期约切换为「拒绝」 ,期约封装的异步会生成某个值,例如期约被拒绝,程序就会拿到拒绝的理由,可能是一个error对象,包含http状态码,相反兑现了,程序也会拿到一个json字符串 ,所以为了支持这两种用例,每个期约只要切换为兑现,就会有一个私有的内布置,相反切换为拒绝,就会有一个私有的内部理由
- 切换期约状态:
- 调用 resolve() 会 把状态切换为兑现,
- 调用 reject() 会把状态切换为拒绝。另 外,调用 reject() 也会抛出错误
let p = new Promise((resolve, reject) => { setTimeout(reject, 10000);
// 10秒后调用
reject() // 执行函数的逻辑
});
setTimeout(console.log, 0, p); // Promise <pending>
setTimeout(console.log, 11000, p); // 11秒后再检查状态
// (After 10 seconds) Uncaught error
// (After 11 seconds) Promise <rejected>
小结:这是一个利用定时器防止死循环的案例,11秒后他会把期约切换到拒绝状态,如果在这个时间内有成功或者失败,状态不会改变,但是这段时间期约状态没有变化,则会修改为拒绝,从而终止,
Thenable 接口
- 在js暴露的异步结构中,任何对象都有一个then()方法,这个方法被认为实现了 Thenable 接口
Promise.prototype.then()
- then() 方法接收最多两个参数: onResolved 处理程序和 onRejected 处理程序。这两个参数都是可选的,如果提供的话,则会在期约分别进入“兑现”和“拒 绝”状态时执行
Promise.prototype.catch()
-用于给期约添加拒绝处理程序。这个方法只接收一个参数: onRejected 处理程序。事实上,这个方法就是一个语法糖,调用它就相当于调用 Promise.prototype.then(null, onRejected)
promise 的then方法
一个Promise必须提供一个then方法来获取其值或原因。 Promise的then方法接受两个参数:
- onFulfilled 和 onRejected都是「可选参数」:如果他们「不是一个函数」则直接「忽略」
- onFulfilled :
- 他必须在promise和fulfilled后调用,且promise的value为其第一个参数
- 他不能promise fulfilled前调用
- 不能多次调用
- onFulfilled和 onRejected只允许在execution context栈仅包含平台代码时执行,
对于一个promise,「他的then方法可以被多次调用」,
- 当promise fulfilled 后 , 所有onFulfilled都必须按照其注册顺序执行
- 当promise rejected后,所有onrejected都必须按照其注册顺序执行
then必须返回一个promise
- 如果返回了值x,则执行promise,则执行promise,解析流程[[Resolve]] (promise2,x)
- 如果抛出异常e,则promise2则应当以e为reason被拒绝
- 如果 onFulfilled 不是一个函数,而且promise1已经fulfilled,则promise2必须以promise1的值fulfilled,
- 如果onReject不是一个函数,且promise1已经rejected,则promise2必须以相同的reason被拒绝
promise的解析过程
是以一个promise和一个值做为参数的抽象过程,可表示为[[Resolve] ] (promise, x).
如果promise 和 x 指向相同的值, 使用 TypeError做为原因将promise拒绝
如果 x 是一个promise, 采用其状态 [3.4]
如果x是pending状态,promise必须保持pending走到x fulfilled或rejected
如果x是fulfilled状态,将x的值用于fulfill promise
如果x是rejected状态, 将x的原因用于reject promise
如果x是一个对象或一个函数
将 then 赋为 x.then. [3.5]
如果在取x.then值时抛出了异常,则以这个异常做为原因将promise拒绝
如果 then 是一个函数, 以x为this调用then函数, 且第一个参数是resolvePromise,第二个参数是rejectPromise
当 resolvePromise 被以 y为参数调用, 执行 [[Resolve]] (promise, y) .
当 rejectPromise 被以 r 为参数调用, 则以r为原因将promise拒绝。
如果 resolvePromise 和 rejectPromise 都被调用了,或者被调用了多次,则只第一次有效,后面的忽略
如果在调用then时抛出了异常
如果 resolvePromise 或 rejectPromise 已经被调用了,则忽略它
否则, 以e为reason将 promise 拒绝