当前位置: 首页 > 后端技术 > Node.js

关于Promise系列的一些小玩意(一)

时间:2023-04-03 18:16:39 Node.js

promise这个词,如果从英文意思来理解,就是“承诺”的意思,所以你可以认为这玩意要做的就是做一层js代码封装保证在下面的逻辑中可以实时获取成功或失败的结果,这正是promise的意思。基本用法constp=newPromise(function(resolve,reject){...})让我们看看这个promise有什么?显然,三个实例方法绑定到promise的原型:它们是thencatchfinally。接下来我们看看这三个实例方法的具体用法。这里我只做一个简单的介绍。使用constp1=newPromise((res,reject)=>{res('success');});p1.then(function(res){console.log(res,'success');//打印成功},function(err){console.log(err,'fail')});constp2=newPromise((res,reject)=>{reject('failed');});p2.then(function(res){console.log(res,'success');},function(err){console.log(err,'fail')//打印失败});这是ES2015promise的正常用法,当然我们也可以连续写;像下面的newPromise((res,reject)=>{...}).then(onFulFilled=()=>{}).catch(onRejected=()=>{}).finally(onFinal=()=>{})无论promise最终结果如何(执行或成功或失败),都会去到finallyhook函数。如果我们需要在异步操作中履行承诺的结果怎么办?解决方案是否可行?带着问题看下面的代码:constp3=newPromise((res,reject)=>{setTimeout(()=>{res('异步调用promise成功');},2000);});p3.then(res=>{console.log(res,'===async_promise');//2秒后打印异步调用promise成功}).catch(err=>{//这里不会来,因为promise调用了resolvesuccess的钩子函数})我们从上面的demo中发现,如果promise的执行结果结束,最后只会成功或者失败,不会有并发结果,同时也支持异步calls+1很好像下面的用法,我们可以依次打印0,1,2,3,4,5constarr=[]constoutput=i=>newPromise(res=>{setTimeout(()=>{console.log(i)res()},1000*i)})for(leti=0;i<5;i++){arr.push(output(i))}Promise.all(arr).然后(()=>console.log(5));如果你熟悉es6(下面简称ES2016)中async&await的使用,你也可以这么优雅地使用constsleep=()=>newPromise(res=>setTimeout(res,1000))(asyncfunction(){for(leti=0;i<5;i++){awaitsleep()console.log(i)}})();以上要求还是可以实现的;你现在喜欢吗?答应了。说到这里,我觉得是时候了解下promise的内部实现原理了。只有了解了内部原理,我们才能轻松愉快地编码。.....废话不多说,直接干活。所以第一步(傻傻的记不住怎么开始,看我们是怎么用promises开始的);因为有newPromise(function(res,rej){});那么应该有一个高端版本的newMyPromise(function(res,rej){});哈哈,这就是我们接下来要定义的类名。继续!classMyPromise{}....大功告成,别逗了,继续,童鞋们。这个类应该有自己的小东西,比如上面提到的一些实例方法,then,catch,finally等等。..继续,应该是classMyPromise{constructor(){}then(resolve=()=>{},rej=()=>{}){}}好了,不折腾了,直接写代码步骤循序渐进,我会记下,只要你仔细阅读,应该很容易,因为你很优秀!类MyPromise{constructor(fn){this.state='PEDDING';//我们定义一个state来表示promise的进度,初始化正在进行,有两个等待成功和失败this.value=undefined;//接收回调的值,给它undefined进行初始化//是的,最重要的是我们的fn实例传入的函数,初始化需要调用一次fn(this.resolve.bind(this),this.reject.bind(this));}resolve(value){if(this.state==="PEDDING"){这个。状态=“已解决”;这。价值=价值;}}reject(value){if(this.state==="PEDDING"){this.state="REJECTED";this.value=值;}}then(resolve=()=>{},reject=()=>{}){//resolve,reject是我们自己传递的两个函数参数if(this.state==="RESOLVED"){resolve(this.value);}if(this.state==="REJECTED"){拒绝(这个。值);}}}完成后,让我们测试一下。一个简单的promise基本原型就出来了,是不是很有成就感呢!顺便试试异步调用。...来来来,来一波代码。varasyncP=newMyPromise((res,rej)=>setTimeout(()=>res(123),2000));异步P。then(r=>console.log(r,'====r'));马的情况未定义,很头疼。.....嗯,一定有问题。你可以把上面的代码断点试试看。原来state的状态一直处于'PEDDING'状态,所以代码不会进入success或者failure钩子函数。看来我们需要一种存储机制来管理这些不确定的状态码,以供后续使用。至少我可以执行异步调用。废话不多说,直接上代码。刚刚发布了一个功能比较完善的自定义promise。....classMyPromise{constructor(fn){this.resolvedCallbacks=[];//缓存成功的钩子函数this.rejectedCallbacks=[];//缓存失败的钩子函数this.state='PEDDING';//我们定义一个state表示promise的进度,初始化正在进行,有两个等待成功和失败this.value=undefined;//接收回调的值,给它undefined进行初始化//是的,最重要的是这个fn我们在例子中传递的函数,初始化需要调用一次fn(this.resolve.bind(this),this.reject.bind(this));}resolve(value){if(this.state==="PEDDING"){this.state="RESOLVED";this.value=值;//缓存中成功的钩子函数去this.resolvedCallbacks.forEach(cb=>cb());}}reject(value){if(this.state==="PEDDING"){this.state="REJECTED";this.value=值;//缓存中失败的钩子函数去this.rejectedCallbacks.forEach(cb=>cb());}}then(resolve=()=>{},reject=()=>{}){//resolve,reject是我们传过来的两个函数参数this.resolvedCallbacks.push(function(){resolve(that.value);});这。拒绝回调。推(函数(){拒绝(那个。值);});}if(this.state==="RESOLVED"){resolve(this.value);}if(this.state==="REJECTED"){reject(this.value);}}}你应该可以展示一波操作。测试开始,之前的异步调用显然没问题,这次2秒后hook函数正确执行。..该喝水了,我渴了。ES5promises并不是一个完美的解决方案,因为如果有一种情况我们需要一个或多个promises的最终结果,无论它们是失败还是成功。如果是怎么解决呢?ES6对这部分有更好的解决方案。..下期再说说如何更优雅的使用promise