最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Promise和异步编程

    正文概述 掘金(黄sir儿)   2021-03-21   579

    个人感悟

        经过一个星期业务的摧残,就算学习完了拉勾的课程,也没有办法动手,趁着周六总算可以把手写Promise的心愿完成了,所有学习内容都在这回代码注释里面了.前面一大段是总的开发过程 然后在代码里注释相应代码的作用.终于掌握了promise,拉勾这个课程俺还是觉得蛮好的解决了面试有关Promise的痛点,以后这方面的面试题也都不用担心了。

        在撸代码前先把异步编程相关知识点补上。

    异步编程

        javascript是单线程,是为了防止对对dom进行误操作。但浏览器是多线程的,它包含:GUI线程、js引擎线程、定时触发器线程、事件触发线程、异步http请求线程。为了确保UI渲染准确,UI渲染线程与JavaScript引擎为互斥的关系。

        javascript分位同步任务和异步任务,异步模式 不会去等待这个任务的结束才开始下一个任务,都是开启过后就立即往后执行下一个任务。耗时函数的后续逻辑会通过回调函数的方式定义。

    事件循环

    异步任务又分为宏任务和微任务,每一次执行栈清空后,先将微任务队列的回调事件推入任务栈(微任务重新产生的微任务也在这次循环中执行),等任务栈清空后再推入一个宏任务。
    微任务:Promise MutationObserver queueMicroTask
    宏任务:setTimeout setInterval script

    Promise和异步编程

    手写Promise

    //Promise是一个类
    //Promise有状态,状态有三种类型 pending, fulfilled, rejected
    //promise有两个基础方法 resolve 和 reject
    //resolve和reject都是会改变Promise状态
    //resolve会从pending->fulfilled,reject会从pending->rejected,状态一旦改变就不能再次改变
    //Promise的构造函数接收一个执行器,该执行器接收两个参数resolve、reject,实际执行的Promise类的方法
    //Promise有一个then方法,接受两个回调,一个成功回调,一个失败回调,根据状态判断执行哪个回调,回调参数是可选的(也就是可传可不传)
    //resolve和reject会把接受的参数传到then回调里,所以这里需要在类里声明一个value和一个reason去保存参数
    //then返回的是一个Promise(链式调用最关键的点),而且要延迟执行,这个Promise的状态就是在这次then执行里面改变的
    //兼容Promise里面是延迟执行的case,把成功和失败函数缓存起来,因为一个promise可以有多个then所以用数组缓存
    //当执行resolve和reject的时候要清空缓存回调函数数组,执行resolve或reject的时候使用shift方法清空数组
    //then返回的Promise对象状态是由回调函数结果决定,如果回调函数返回一个普通对象,then则返回一个resolve状态,值为返回对象的Promise对象,如果返回一个Promise对象则将这个Promise对象的状态和值传递给then返回的Promise,且这个Promise对象不可以是自己
    //为了实现上一步所以需要写一个方法resolvePromise做处理
    //执行器步骤都需要错误处理
    //then到这一步就结束了
    
    //Promise.all()的实现,因为能通过类直接调用说明是静态方法,需要用static关键字声明
    //Promise.all()接收一个数组(可以是普通对象也可以是Promise对象)
    //当所有参数数组里所有Promise对象执行完成后返回一个fullfilled状态的Promise,值为Promise返回的值的数组,只要参数数组里有一个抛出一场则返回一个rejected的Promise对象
    //在Promise.all里面会把参数数组里的Promise对象和普通对象分开处理,如果可以用Promise.resolve另说
    //Promise.all的核心思想就是返回一个Promise对象,在该对象的执行器中遍历参数数组并执行,把结果放入到结果数组里,同时需要一个计数器判断是否完成
    
    //race方法差不多
    
    //Promise.resolve如果参数是Promise对象就返回参数,如果不是Promise对象就返回一个,也是个静态方法
    
    //finally方法,接受一个函数参数,无论Promise对象状态如何都会执行这个函数参数,且返回当前Promise值,这个时候就可以借助then方法,而且当这个回调函数返回一个Promise时要等这个返回的Promise执行完才继续执行后续的then
    
    //catch方法,接受一个函数参数返回一个Promise值,该Promise回调返回值决定,也借助then方法
    
    
    const PENDING = 'pending';
    const FULFILLED = 'fulfilled';
    const REJECTED = 'rejected';
    
    //Promise是一个类
    class LrbPromise {
        status = PENDING;  //Promise有状态,状态有三种类型 pending, fulfilled, rejected
        //resolve和reject会把接受的参数传到then回调里,所以这里需要在类里声明一个value和一个reason去保存参数
        value = void 0;
        reason = void 0;
        successCallbackList = [];
        failCallbackList = [];
        constructor(executor) { //Promise的构造函数接收一个执行器,该执行器接收两个参数resolve、reject,实际执行的Promise类的方法
            try { //执行器步骤都需要错误处理
                executor(this.resolve, this.reject);
            } catch (e) {
                this.reject(e);
            }
        }
        resolve = (value) => {  //注意这里用尖头函数保留this
            if (this.status !== PENDING) return; //状态一旦改变就不能再次改变
            this.status = FULFILLED; //resolve会改变Promise状态,从pending->fulfilled
            this.value = value; //resolve和reject会把接受的参数传到then回调里,所以这里需要在类里声明一个value和一个reason去保存参数
            while(this.successCallbackList.length) { //当执行resolve和reject的时候要清空缓存回调函数数组,执行resolve或reject的时候使用shift方法清空数组
                this.successCallbackList.shift()()
            }
        }
        reject = (reason) => { //注意这里用尖头函数保留this
            if (this.status !== PENDING) return; //状态一旦改变就不能再次改变
            this.status = REJECTED; //reject会改变Promise状态,从pending->rejected
            this.reason = reason; //resolve和reject会把接受的参数传到then回调里,所以这里需要在类里声明一个value和一个reason去保存参数
            while(this.failCallbackList.length) { //当执行resolve和reject的时候要清空缓存回调函数数组,执行resolve或reject的时候使用shift方法清空数组
                this.failCallbackList.shift()()
            }
        }
        //Promise有一个then方法,接受两个回调,一个成功回调,一个失败回调,根据状态判断执行哪个回调,回调参数是可选的(也就是可传可不传)
        then (successCallback, failCallback) {
            successCallback = successCallback ? successCallback : value => value; //回调参数是可选的(也就是可传可不传)
            failCallback = failCallback ? failCallback : e => { throw(e) }
            const promise2 = new LrbPromise((resolve, reject) => { // 注意这里用尖头函数保留this,then返回的Promise对象状态是由回调函数结果决定
                if (this.status === FULFILLED) { //如果是fulfilled状态执行成功回调
                    setTimeout(() => { //注意这里用尖头函数保留this
                        try { //执行器步骤都需要错误处理
                            let x = successCallback(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    });
                } else if (this.status === REJECTED) { //如果是rejected状态执行成功回调
                    setTimeout(() => {
                        try { //执行器步骤都需要错误处理
                            let x = failCallback(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    });
                } else {
                    //兼容Promise里面是延迟执行的case,把成功和失败函数缓存起来,因为一个promise可以有多个then所以用数组缓存
                    this.successCallbackList.push(() => {
                        setTimeout(() => { //注意这里用尖头函数保留this
                            try { //执行器步骤都需要错误处理
                                let x = successCallback(this.value);
                                resolvePromise(promise2, x, resolve, reject);
                            } catch (e) {
                                reject(e)
                            }
                        }, 0)
                    });
                    this.failCallbackList.push(() => {
                        setTimeout(() => { //注意这里用尖头函数保留this
                            try { //执行器步骤都需要错误处理
                                let x = failCallback(this.reason);
                                resolvePromise(promise2, x, resolve, reject);
                            } catch (e) {
                                reject(e)
                            }
                        }, 0)
                    });
                    // this.failCallbackList.push( () => {
                    //     setTimeout(() => {
                    //         try {
                    //             let x = failCallback(this.reason);
                    //             resolvePromise (promise2, x, resolve, reject);
                    //         } catch (e) {
                    //             reject(e)
                    //         }
                    //     }, 0)
                    // });
                }
            })
            return promise2;
        }
        //Promise.resolve如果参数是Promise对象就返回参数,如果不是Promise对象就返回一个,也是个静态方法
        static resolve (value) {
            return value instanceof LrbPromise ? value : new LrbPromise(resolve => resolve(x));
        }
        //Promise.all()的实现,因为能通过类直接调用说明是静态方法,需要用static关键字声明,接受一个参数数组
        //Promise.all的核心思想就是返回一个Promise对象,在该对象的执行器中遍历参数数组并执行,把结果放入到结果数组里,同时需要一个计数器判断是否完成
        static all (arr) {
            return new LrbPromise((resolve, reject) => {
                if (!Array.isArray(arr))  return reject(new TypeError('请传入数组'));
                const result = [];
                let counter = 0;
                //当所有参数数组里所有Promise对象执行完成后返回一个fullfilled状态的Promise,值为Promise返回的值的数组,只要参数数组里有一个抛出一场则返回一个rejected的Promise对象
                function addValue (i, val) {
                    result[i] = val;
                    counter++;
                    if (counter === arr.length) {
                        resolve(result)
                    }
                }
                for (let i = 0; i < arr.length; i++) {
                    //在Promise.all里面会把参数数组里的Promise对象和普通对象分开处理,如果可以用Promise.resolve另说
                    if (arr[i] instanceof LrbPromise) {
                        arr[i].then(res => {
                            addValue(i, res);
                        }, reject)
                    } else {
                        addValue(i, arr[i]);
                    }
                }
                //如果存在Promise.resolve
                // for (let i = 0; i < arr.length; i++) {
                //     LrbPromise.resolve(arr[i]).then(res => {
                //         result[i] = res;
                //         counter++;
                //         if (counter === arr.length) {
                //             resolve(result)
                //         }
                //     }, reject)
                // }
            })
        }
        //finally方法,接受一个函数参数,无论Promise对象状态如何都会执行这个函数参数,且返回当前Promise值,这个时候就可以借助then方法,而且当这个回调函数返回一个Promise时要等这个返回的Promise执行完才继续执行后续的then
        finally (callback) {
            return this.then((value) => {
                return LrbPromise.resolve(callback()).then(() => value)
            }, (reason) => {
                return LrbPromise.resolve(callback()).then(() => { throw reason })
            })
        }
        //catch方法,接受一个函数参数返回一个Promise值,该Promise回调返回值决定,也借助then方法
        catch (callback) {
            return this.then(void 0, callback)
        }
    }
    
    //then返回的Promise对象状态是由回调函数结果决定,如果回调函数返回一个普通对象,then则返回一个resolve状态,值为返回对象的Promise对象,如果返回一个Promise对象则将这个Promise对象的状态和值传递给then返回的Promise,且这个Promise对象不可以是自己
    function resolvePromise (promise, x, resolve, reject) {
        if (promise === x) {
            reject(new TypeError('不可以返回自己')); //这里可以试试直接throw
        }
        if (x instanceof LrbPromise) {
            x.then(resolve, reject);
        } else {
            resolve(x);
        }
    }
    
    function getPromise () {
        return new LrbPromise((resolve, reject) => {
            // resolve(1)
            // setTimeout(() => {
            //     resolve('成功')
            // });
                // resolve('成功')
            reject('失败')
        })
    }
    
    const p2 = getPromise();
    
    p2.then()
        .then(res => console.log('成功:', res), reason => console.log('失败:', reason))
    // const p1 = new LrbPromise((resolve, reject) => {
    //     // resolve(1)
    //     // reject(1)
    // }).then((value) => {
    //     console.log(value);
    // }, (reason) => {
    //     console.log(reason);
    // })
    

    起源地下载网 » Promise和异步编程

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元