zoukankan      html  css  js  c++  java
  • JS实现Promise原理

    promise是用来解决Js中的异步问题的,js中所有的异步可从callback → promise → generator + co = async + await

    其实所有的都是callback的变相处理,只是后者慢慢变的越来越优雅和强壮可扩展。

    那么如何实现promise呢?先观察一下promise的样子

    let  a = new Promise((resolve,reject)=>{
           // dosomething
           resolve()  
    })

    无非是一个名称叫Promise的对象,然后传参一个函数 (resolve,reject)=>{resolve()}

    promise的强大之处在于回调处理,promise.then(resolveFunticon,rejectFunction)

    第一个函数是resolve的回调处理函数,第二个是reject

    说到resovle,reject,那么还得再提一下pending,promise对象内部有以上三种状态。默认是pending状态

    状态的转变需要我们手动自己去调用函数resolve()或者reject()

    整理一下思路:new promise的时候callback作为传参参数执行promise函数,callback在promise函数内部执行。callback里手动调用的resolve或者reject方法,那么promise对象里肯定是有这两种方法的。

    最关键的是记住状态,然后再then的时候根据状态去判断到底是执行resolveFunticon还是rejectFunction。

    function PromiseFn(callBack){
            let self = this;
            self.resolveVal = undefined;
            self.rejectVal = undefined;
            self.status = 'pending'//默认是等待状态
            
            function resolve(val){
                if(self.status === 'pending'){
                    self.resolveVal = val;
                    self.status = 'resolve'
                }
            }
            function reject(val){
                if(self.status === 'pending'){
                    self.rejectVal = val;
                    self.status = 'reject'
                }
            }
         //这里要注意,try catch若是写在 let self = this 会报错,let存在暂时死区,没有常规的变量提升。必须先申明后使用
         // callback执行,调用resolve函数。
    try{ callBack(resolve,reject)//callback进来自动执行 }catch(e){ reject()//若是有报错,则直接变为reject状态 } }

    上面关于let的深入可以去看这篇博客:我用了两个月的时候才理解let

    可能会被callback给弄得有点懵,其实把它当做函数fn就行,fn在promise内部执行,然后fn内部刚好有个resolve(),然后再去调用promise的这个resolve执行。

    也就是这个原因,所以callback的参数必须是resolve和reject。

    上面这一步,需要注意的是callback是立即执行的。下面这段会立即打印111

    let  a = new Promise((resolve,reject)=>{
           console.log('111')
    })
    console.log('456')

    上面我们定义了 resolveVal 和 rejectVal,因为待会在promise调用then执行的时候,会作为参数传参给resolveFunticon或者rejectFunction

    所以promise对象内部必须记住这个参数。

    下面一起看看promise.then吧,promise.then(resolveFunticon,rejectFunction)。在promise的原型链上有个then方法

    PromiseFn.prototype.then = function(resolveFunction,rejectFunction){
      let self = this;
      if(self.status === 'resolve'){
    	resolveFunction(self.resolveVal)
      }
      if(self.status === 'reject'){
    	rejectFunction(self.rejectVal)
      }
    }
    

    在then执行的时候,去判断内部是啥状态,然后执行对应的resolve或者reject回调函数。  

    然后运行试一试

    let promiseA = new PromiseFn((resolve,reject)=>{
             resolve('成功啦')
    })
    promiseA.then((resolveVal)=>{
    	console.log('resolve'+resolveVal)
    },(rejectVal)=>{
    	console.log('reject'+rejectVal)
    })
    

    结果是:  

    但是这样就结束了吗?no,我们一般对Promise的使用场景可能是下面这样的,需要考虑异步问题。

    let promiseA = new PromiseFn((resolve,reject)=>{
    	setTimeout(function(){
    		resolve('成功啦')
    	},2000)
    })
    

    这里用settimeout代替一下,一般我们都是一个ajax请求,然后在请求到结果后去resolve.

    上面的写法显然是无法满足这种条件的,当去执行promise.then的时候,发现状态还是pending,不做任何执行。

    那我们怎么去做呢?发现状态是pending,肯定是两秒钟之内啦,不妨先把promise.then两个函数参数先在promise里面记下来,两秒钟后状态变为resolve了,再执行函数。

          function PromiseFn(callBack){
    		try{
    			callBack(resolve,reject)
    		}catch(e){
    			reject()
    		}
    		let self = this;
    		self.resolveVal = undefined;
    		self.rejectVal = undefined;
    		self.status = 'pending'//默认是等待状态
    		self.keepResolveFn = []//
    		self.keepRejectFn = []//
    		
    		function resolve(val){
    			if(self.status === 'pending'){
    				self.resolveVal = val;
    				self.status = 'resolve';
    				self.keepResolveFn.forEach(fn=>fn());
    			}
    		}
    		function reject(val){
    			if(self.status === 'pending'){
    				self.rejectVal = val;
    				self.status = 'reject';
    				self.keepRejectFn.forEach(fn=>fn());
    			}
    		}
    		//执行先记录resolve和reject函数事件
    		
    	}
    	PromiseFn.prototype.then = function(resolveFunction,rejectFunction){
    		let self = this;
    		if(self.status === 'resolve'){
    			resolveFunction(self.resolveVal)
    		}
    		if(self.status === 'reject'){
    			rejectFunction(self.rejectVal)
    		}
    		if(self.status === 'pending'){
    			self.keepResolveFn.push(()=>{
    				resolveFunction(self.resolveVal);
            	});
            	self.keepRejectFn.push(()=>{
                	rejectFunction(self.rejectVal)
            	});
    		}
    	}
    	let promiseA = new PromiseFn((resolve,reject)=>{
    		setTimeout(function(){
    			resolve('成功啦')
    		},2000)
    	})
    	promiseA.then((resolveVal)=>{
    		console.log('resolve'+resolveVal)
    	},(rejectVal)=>{
    		console.log('reject'+rejectVal)
    	})
    

    结果是:延迟两秒后,打印出  ‘resolve成功啦’。

    最后多提一句:所有的then方法执行,也是通过return 一个新的promise对象来执行的。这样才有链式回调,new promise.then().then().then()

            var b = 10;
            new Promise((resolve, reject) => {
    
                setTimeout(() => {
                    b += 10;
                    resolve();
    //                reject();
                }, 1000);
    
            }).then(function() {
                console.log(b);
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        b *= 2;
                        resolve();
                    }, 1000);
                });
            }).then(function() {
                console.log(b);
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        b *= b;
                        resolve();
                    }, 1000);
                });
            }).then(function() {
                console.log(b);
            },function() {
                console.log('失败了');
            });
    

      

      

     

  • 相关阅读:
    (转载)C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
    网络编程前奏(三)
    Linux学习之第三课时--Linux目录
    Linux学习之第二课时--linux命令格式及命令概述
    Linux学习之第一课时--linux前生今世
    python学习之第十四课时--基本数据练习
    python学习之第十三课时--其他数据类型,其他
    python学习之第十二课时--基本数据类型(set)
    python学习之第十一课时--基本数据类型(dict)
    python学习之第十课时--基本数据类型(tuple)
  • 原文地址:https://www.cnblogs.com/hjj2ldq/p/9489598.html
Copyright © 2011-2022 走看看