promise是怎么个事?
讲个故事先,我跟你借钱5万,承诺(Promise)半年后还钱。于是,你一边打工coding一边等待(pending)半年。那么,半年后会有2种可能,我还钱或者不还钱,列举如下:
1. 半年后我还钱,这个promise被我完成(fulfilled)了,我会给你钱(value);
2. 半年后我没钱,这个半年之约就被作废了(rejected)了,我会给你解释一下为啥还不了钱(reason)。
3. 无论半年后结果如何,我仍然厚脸皮跟你玩,我们还是(finally)好朋友哈哈哈哈
// 把上面的例子写成代码那就是:
let yourMoney = new Promise((giveYou, notGive) =>{
//这里是你在coding打工,等半年之约呢
//下面是半年后可能发生的情况
giveYou('some money') //给你钱,这个参数就是卡,到.then里去处理取钱
notGive('one letter') //不给你钱,这个参数是一封信,要骂我先到.catch里去看看原因
})
yourMoney.then(haveMoneyFn(value){
console.log(value) //打印出:给你钱,这个参数就是卡,到.then里去处理取钱
}, then的第二个参数是优先执行失败函数的).catch( haveLetterFn(letter){
console.log(letter) //打印出:不给你钱,这个参数是一封信,要骂我先到.catch里去看看原因
}).finally(() =>{
console.log('无论如何,这句话一定会打印!我们还是好朋友')
})
作废情况:优先进.then的第二个参数里执行,如果只有1个,那就去catch执行
一 promise规范
记规范的意义在于,可以手写实现一个promise,面试官可以提这种要求。
1. 术语
- promise:有then方法的对象或者函数
- thenable:是一个有then方法的对象或函数
- value: promise状态成功时的值resolve(valuue) string number boolean undefined thenable promise
- reason: promise失败时的值reject(reason)
- exception: 使用throw抛出的异常
2. 规范
promise states
3种状态,流转关系。
- pending
1.1 初始状态,可改变
1.2 在resolve和reject之前都处于这个状态
1.3 resolve -> fulfilled
1.4 reject -> rejected - fulfilled
2.1 最终态,不可变
2.2 promise被resolve之后会变这个状态
2.3 必须有一个value值,可以是任意的js合法值 - rejected
3.1 也是最终态,不可变
2.2 promise被reject之后
2.3 必须有一个reason值
then
每个promise都可以调用.then方法,.then其实就是promise的一个出口。
规范定义了promise应该提供一个.then方法,用来访问最终的结果,不论是value还是reason。
promise.then(onfulfilled, onrejected).then(...).then(...)
-
参数规范
onFulfilled和onRejected类型都必须是function,否则,值要被中转到下一个.then -
onFulfilled特性
2.1 promise的状态变成fulfilled时,应该调用onFulfilled,这个函数的参数是value
2.2 promise状态变之前,不该调佣
2.3 只能调用一次(如何实现只调用一次?) -
onRejected特性
同上,状态是rejected时调用,这个函数的的参数是reason,只能调用一次。 -
onfulfilled和onRejected都在微任务阶段执行
微任务执行,这是规范里定义的。
那么,实现promise的时候,怎么生成微任务? -
then方法可以调用多次 5.1 onFulfilled回调都按照注册顺序执行,也就是按照.then的顺序执行。 5.2 onrejected同上,按照.then顺序执行
-
返回值 .then应该返回一个promise
const promise2 = promise.then(onfulfilled, onrejected)
规范:
6.1 当onFulfilled或onRejected 执行结果是x,调用"resolvePromise"
6.2 如果没有正常的返回执行结果,抛出了异常,promise2的结果需要被reject
6.3 如果onFulfilled参数不是函数,那参数值要当做promise2的fulfilled参数
6.4 同样的,onRejected参数非函数,要把reason做promise2的reject参数
7. resolvePromise promise2是第一个promise的返回值
resolvePromise(promise2, x, resolve, reject)
规范:
7.1 如果x === promise2,那么报错,reject error
7.2 如果x是一个promise,会有3个状态
7.3 如果x是一个Object、function,
那么获取x.then()很小可能会报错,,那么reject reason
是一个function,那么then.call(x, resolvePromiseFn, rejectPromiseFn)
二 实现promise
1. new Promise() 代表Promise是一个构造函数或者class
2. 定义三种状态
3. constructor定义初始状态,status、value、reason
4. resolve和reject方法
4.1 这两个 方法可修改状态status
4.2 入参,value和reason
5. 对于实例化promise的入参的处理
5.1 入参是一个函数,接受resolve和reject两个参数
5.2 初始化promise的时候,就要**同步**执行这个参数函数,有任何报错都要reject抛出去
6. then方法
6.1 then接受2个参数,onFulfilled和onRejected
then(onFulfilled,onRejected)
6.2 检查参数,非函数则忽略并传入下个then
6.3 根据函数状态,调用对应的函数
6.4 此时的status按理应该是“fulfilled”||“rejected”,但是,如果promise的同步代码里有setTimeout异步执行,会影响status变更的时机,导致.then执行时status还是pending。如此,就要把各自的cb都放到一个列表里依次执行。=》设置2个数组,fulfilled_cb=[]和rejected_cb=[]
6.5 然后应对.then时status还是pending的情况,要监听status的变化,变了就执行对应cb。用到getter, setter劫持。
7. then的返回值
7.2 返回值应该是一个Promise(这是规范定义)
7.2 如果onFulfilled和onRejected抛出异常e,那么.then返回的promise一定要reject e
7.3如果onFulfilled不是函数,且promise1成功执行,那么promise2必须返回同样的状态和value(这也是规范定义)
7.4 如果onRejected不是函数,且promise1拒绝执行,promise2必须返回同样的值和reason(同上规范定义)
7.5 onFulfilled||onRejected返回x,则运行resolvePromise
8. resolvePromise
9.onFulfilled和onRejected是在微任务里执行,放到queueMicrotask里
三 generator & async
Iterator
Iterator迭代器是es6引入的一种遍历机制,也是一种特殊对象。每个迭代器有next()方法,每次调用返回当前结果对象,这个结果对象中有2个值: 1.value,当前属性的值; 2.done,遍历是否结束,是布尔值,结束=true
生成器generator
是一种返回迭代器的函数,写法:function * generator(){...},内有关键字yield:
function * generator(){
const list = [1, 2, 3]
for(let i of list){
yield i
}
}
let g = generator()
console.log(g.next) //{value:1 , done:false}
console.log(g.next) //{value:2 , done:false}
console.log(g.next) //{value:3 , done:false}
console.log(g.next) //{value:undefined , done:true}
next调用之后函数会自动停止执行,直到再次调用next