前端爪1-promise规范和应用-手把手写promise

83 阅读5分钟

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种状态,流转关系。

  1. pending
    1.1 初始状态,可改变
    1.2 在resolve和reject之前都处于这个状态
    1.3 resolve -> fulfilled
    1.4 reject -> rejected
  2. fulfilled
    2.1 最终态,不可变
    2.2 promise被resolve之后会变这个状态
    2.3 必须有一个value值,可以是任意的js合法值
  3. 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(...)
  1. 参数规范
    onFulfilled和onRejected类型都必须是function,否则,值要被中转到下一个.then

  2. onFulfilled特性
    2.1 promise的状态变成fulfilled时,应该调用onFulfilled,这个函数的参数是value
    2.2 promise状态变之前,不该调佣
    2.3 只能调用一次(如何实现只调用一次?

  3. onRejected特性
    同上,状态是rejected时调用,这个函数的的参数是reason,只能调用一次。

  4. onfulfilled和onRejected都在微任务阶段执行
    微任务执行,这是规范里定义的。
    那么,实现promise的时候,怎么生成微任务?

  5. then方法可以调用多次 5.1 onFulfilled回调都按照注册顺序执行,也就是按照.then的顺序执行。 5.2 onrejected同上,按照.then顺序执行

  6. 返回值 .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