手写promise

Posted betterangela

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写promise相关的知识,希望对你有一定的参考价值。

手写promise

高阶函数:应用最多的

发布订阅:观察者模式

promise实现原理

常见面试题

node 11.13 每个版本的事件环 & API 都有区别

高阶函数

函数套函数

如果函数的参数或返回值是一个函数,那我们就把这个函数叫做高阶函数

要写前端,最核心的就是函数

这里有AOP面向切片编程的思想

在原来逻辑之前切了一刀

作用域产生是根据函数定义的位置,执行时产生的是执行上下文

作用域是定义的时候就产生的

console.log('promise====================================');

function say(who) 
    console.log(who+' say hello');

Function.prototype.before=function (fn) 
    return (...args)=>
        fn()
        this(...args)
    

let newFn=say.before(function () 
    console.log('say hello before');
)
newFn('Tom')

let oldPush=Array.prototype.push;
Array.prototype.push=function (...args) 
    console.log('push 触发...')
    console.log(this)
    oldPush.call(this,...args)

let arr=[1,3,5]
arr.push(7)
console.log(arr,arr)

面向切片编程AOP

我只关心核心逻辑,需要增强就增强一下就好了

这里涉及到闭包 执行上下文没有被销毁

all.js 本质是闭包原理

all-on-emit 发布订阅模式

发布订阅模式

核心思路:

订阅的时候把函数存到一个数组里面,然后发布的时候一个一个依次执行。

我先订阅好这件事情,订阅的时候事件是不会立即执行的。

发布订阅模式的特点:发布和订阅是没有关系的,两者是并行的关系

观察者模式

观察者存到被观察者中

写框架用这个用得比较多,写核心逻辑最多是用发布订阅

Promise*

基础用法

then链

异步代码中的报错是无法被外部try-catch捕获的

代码思路:

首先then需要返回一个新的promise

然后我的执行结果还要传递给下一个then,所以promise2要变为成功态,所以要拿到执行结果x然后resovle

resolvePromise

判断数据类型:

typeof:不能判断具体的引用数据类型和null 数组也是object

constructor: 判断构造函数

instanceof:在原型链上找

toString:万能

const PENDING='PENDING';
const RESOLVED='RESOLVED';
const REJECTED='REJECTED';

const resolvePromise=function (promise2,x,resolve,reject) 
    if(promise2===x)
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    
    if(typeof x === 'object' && x!=null || typeof x ==='function')
        try
            let then=x.then;
            if(typeof then ==='function')
                then.call(x,y=>
                    // resolve(y)
                    // 如果y又是一个promise,那么继续递归
                    resolvePromise(promise2,y,resolve,reject)
                ,r=>
                    reject(r)
                )
            else
                resolve(x)
            
        catch (e) 
            reject(e)
        
    else
        resolve(x)
    


class Promise 
    constructor(executor)
        this.status=PENDING;
        this.value=undefined;
        this.reason=undefined;
        this.onResovledCallbacks=[]
        this.onRejectedCallbacks=[]
        let resolve=(value) => 
            if(this.status===PENDING)
                this.status=RESOLVED;
                this.value=value;
                // 成功
                this.onResovledCallbacks.forEach(fn=>fn())
            
        
        let reject=(reason) =>
            if(this.status===PENDING)
                this.status=RESOLVED;
                this.reason=reason;
                // 失败
                this.onResovledCallbacks.forEach(fn=>fn())
            

        
        try
            executor(resolve,reject)
        catch (e) 
            reject(e)
        

    
    then(onresolved,onrejected)
        onresolved=typeof onresolved==='function'?onresolved:(data)=>data;
        onrejected=typeof onrejected==='function'?onrejected:(err)=>
            throw err;
        
        // 每一个then都返回一个新的promise
        let promise2=new Promise((resolve,reject)=>
            if(this.status===RESOLVED)
                setTimeout(()=>
                    try
                        let x=onresolved(this.value)
                        resolvePromise(promise2,x,resolve,reject)
                    catch (e) 
                        reject(e)
                    
                ,0)
            
            if(this.status===REJECTED)
                setTimeout(()=>
                    try
                        let x=onrejected(this.reason)
                        resolvePromise(promise2,x,resolve,reject)
                    catch (e) 
                        reject(e)
                    
                ,0)
            
            if(this.status===PENDING)
                this.onResovledCallbacks.push(()=>
                    setTimeout(()=>
                        try
                            let x=onresolved(this.value)
                            resolvePromise(promise2,x,resolve,reject)
                        catch (e) 
                            reject(e)
                        

                    ,0)
                )
                this.onRejectedCallbacks.push(()=>
                    setTimeout(()=>
                        try
                            let x=onrejected(this.reason)
                            resolvePromise(promise2,x,resolve,reject)
                        catch (e) 
                            reject(e)
                        
                    ,0)
                )
            
        )
        return promise2;
    



Promise.defer=Promise.deferred=function()
    let dfd=
    dfd.promise=new Promise((resolve,reject)=>
        dfd.resolve=resolve;
        dfd.reject=reject;
    )
    return dfd;

function isPromise(obj)
    if(typeof obj ==='object' && obj !=null || typeof obj==='function')
        if(obj.then && typeof obj.then ==='function') return true;
    
    return false;

Promise.all=function(values)
    return new Promise((resolve,reject)=>
        let results=[],times=0;
        function processData(index,data) 
            times++;
            results[index]=data;
            if(times===values.length)
                resolve(results)
            
        

        for(let i=0;i<values.length;i++)
            let current=values[i];
            if(isPromise(current))
                current.then(res=>processData(i,res))
            else
                processData(i,values[i])
            
        
    )

module.exports=Promise;

finally

finally会承接上一个promise的状态,透过finally,传递给下一个then/catch

finally就是then的别名,就是通过then去实现的。如果上一个是成功就把成功往下传,上一个是失败就把失败往下传

Generator

迭代器,就是一个对象,具有next方法,并且next方法返回value:xxx,done:xxx

生成器就是产出迭代器,就是生成迭代器

let obj=
    0:'a',
    1:'b',
    length:2,
    *[Symbol.iterator]()
        for(let i=0;i<this.length;i++)
            yield this[i]
        
        // let i=0;
        // return 
        //     next:()=> // 直接使用箭头函数
        //         return 
        //             value:this[i],
        //             done:i++===this.length
        //         
        //     
        // 
    

let arr=[...obj];
console.log(arr);

generator+co

也就是async/await的原理

const fs=require('fs').promises;
function *read() 
    try
        const content=yield fs.readFile('./name11.txt','utf8');
        const age=yield fs.readFile('./age.txt','utf8');
        return age;
    catch (e) 
        console.log('err=======',e)
    


let it=read();
// const value,done=it.next();
// Promise.resolve(value).then(data=>
//     console.log('data=======');
//     console.log(data)
//     const value,done=it.next(data);
//     Promise.resolve(value).then(data=>
//         console.log('data=======');
//         console.log(data)
//         const value,done=it.next(data)
//     )
// )
// 核心代码
function co(it) 
    return new Promise((resolve,reject)=>
        function deepNext(it,data) 
            const value,done=it.next(data);
            if(!done)
                Promise.resolve(value).then(data=>
                    deepNext(it,data) // 把yield结果传递给下一个it.next
                ,(err)=>it.throw(err))
            else
                resolve(data)
            
        
        deepNext(it)
    )

co(it)

小扩展:

其实还是AOP思想

总结

 

手写Promise

手写Promise

在本文中,我们通过手写实现一个符合Promise/A+规范的Promise来深入了解Promise, 而且手写Promise也是一道大厂面试常考题。在进入正题之前,推荐各位阅读一下Promise/A+规范 (网上有很多解释,可以自行搜索),这样才能更深入的理解本文中的代码。

实现一个简易版的Promise

先来搭建函数的大体框架

// 创建3个常量表示Promise的3个状态
const PENDING = ‘pending‘
const RESOLVED = ‘resolved‘
const REJECTED = ‘rejected‘

function MyPromise(fn){
    // 创建常量that保存this, 因为Promise代码可能会异步执行,用于获取正确的this对象
    const that = this
    // 一开始Promise的状态应该是pending
    this.state = PENDING
    // value变量用于保存resolve或者reject中传入的值
    that.value = null
    // 用于保存then中的回调,因为当执行完Promise时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
    that.resolvedCallbacks = []
    that.rejectedCallbacks = []

    // 待完善 resolve 和 reject 函数
    // 待完善执行 fn 函数
}

接下来完善resolvereject函数,添加在MyPromise内部

// 待完善的 resolve 和 reject 函数
function resolve(value) {
    console.log(‘执行resolve‘)
    // 首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待态才可以改变状态
    if (that.state === PENDING) {
        // // 将当前状态更改为对应状态,并且将传入的值赋值给value
        that.state = RESOLVED
        that.value = value
        // 状态改变时,指定回调
        that.resolvedCallbacks.map(cb => cb(that.value))
    }
}
function reject(value) {
    // 首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待态才可以改变状态
    if (that.state === PENDING) {
        // 将当前状态更改为对应状态,并且将传入的值赋值给value
        that.state = REJECTED
        that.value = value
        // 状态改变时执行回调
        that.rejectedCallbacks.map(cb => cb(that.value))
    }
}

完成以上两个函数后,就该实现如何执行Promise中传入的函数了

// 待完善执行 fn 函数
try {
    // 执行传入的参数并且将之前两个函数当做参数传进去
    fn(resolve, reject)
}catch (e) {
    // 可能执行函数过程中会遇到错误,需要捕获错误并且执行 reject 函数
    reject(e)
}

最后实现较为复杂的then函数

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    console.log(‘执行then‘)
    const that = this
    // 判断两个参数是否为函数类型,因为这两个参数是可选参数,如果不是函数,则直接返回相应的value或reason
    onFulfilled = typeof onFulfilled === ‘function‘ ? onFulfilled : v => v
    onRejected = typeof onRejected === ‘function‘ ? onRejected : r => {
        throw r
    }

    // 当函数是等待态时,就往回调函数数组中push函数
    if (that.state === PENDING) {
        console.log(‘Pending‘)
        that.resolvedCallbacks.push(onFulfilled)
        that.rejectedCallbacks.push(onRejected)
    }
    // 如果不是等待态,就执行相对应的函数
    if (that.state === RESOLVED) {
        onFulfilled(that.value)
    }

    if (that.state === REJECTED) {
        onRejected(that.value)
    }
}

最后,使用MyPromise

new MyPromise((resolve, reject) => {
    // 这里是异步,所以回调会先存入resolvedCallbacks中
    setTimeout(() => {
        resolve(1)
    }, 0)
    }).then(value => {
        console.log(value)
    })

以上就是简单版的Promise实现,接下来实现更为完整版的Promise的解析。

 

以上是关于手写promise的主要内容,如果未能解决你的问题,请参考以下文章

前端面试题之手写promise

前端技能树,面试复习第 52 天—— 手写代码:Javascript 基础考核

前端技能树,面试复习第 53 天—— 手写代码:数据处理 | 排序 | 扁平 | 去重 | 解析 | 分隔

手写promise

手写模拟SpringMvc源码

手写一个 WebSocket 协议