【javascript】手写Promise前必须知道的几个小例子【一】

归档

迅速理清promise运行机制与实际中的应用
利用Promise解决批量文件串行下载问题
不用promise.all只用promise做并行下载文件
手写Promise前必须知道的几个小例子【一】
手写Promise前必须知道的几个小例子【二】
手写Promise分步骤超详尽解答(超越PromiseA+标准)

背景

  • 博主在网上找了很多讲手写promise的资料,发现什么样的实现都有,太乱了,后来听了节某老师讲的手写Promise课,终于收获很多,解决了很多疑惑,准备写几个博客总结下收获。
  • 手写Promise里面有些东西稍微有点绕,先拿几个浅显的例子来实现一些小功能。

一、before与after函数

  • 有这样一个需求,给你个函数,需要你进行包装一下,让他每次调用的时候就会先执行你的before函数,然后执行他本身,再执行after函数,这几个函数间需要传参。
  • 乍一看这个实现就是个装饰器的实现,先来个易懂的实现:
function say(...args){
    console.log('aa',args)
}
Function.prototype.before=function(fn){
    return (...args)=>{fn();this(...args)}
}

const mysay = say.before(()=>{
    console.log('before')
})
mysay(1,2,3)

before
aa [ 1, 2, 3 ]

  • 这个实现里面,把函数原型Function上绑定可传入个函数并返回一个函数,这样就可以调用say.before(fn)来得到这个按自己顺序执行的一个函数,并且可以进行传参。
  • 有人可能会问before为啥不绑到say里,因为绑say上需要new才能调用before,而before函数中的this就是New出来的对象,并不是函数,导致传参不好传,需要this改成this.constructor把参数传进去。但这么做就反而麻烦了。
  • 下面看个一般实际场景的写法:
const myfunc=(mymethod,mywrapper)=>{
    mywrapper.forEach(element => {
        element.before()
    });
    mymethod();
    mywrapper.forEach(element=>{
        element.after()
    })
}
const mywrapper=[{
    before:()=>console.log('before1'),
    after:()=>console.log('after1')
},{   
    before:()=>console.log('before2'),
    after:()=>console.log('after2')
}];
const mymethod=()=>console.log('mymethod');
myfunc(mymethod,mywrapper);

before1
before2
mymethod
after1
after2

  • 这段代码把before和after逻辑抽离出来做成对象,然后做个函数,遍历对象里的方法并执行。当然这个Before和after的mywrapper对象可以配成其他形式,好遍历就可以。
  • 这个写法据说是react事务中的例子,主要学习下思想,先配好前后函数,然后写个函数按顺序遍历即可。

二、柯里化

  • 柯里化这个词名字听着很高端实际概念很简单。博主搜了下网上柯里化一般有2种说法,一种说的比较广义,就是把一个函数拆成多个小函数做出的效果跟直接用一个函数做出的效果一样。第二种说的比较狭义,就是说函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。
  • 其实主要思想都是一样的:1、参数复用;2、提前返回;3、延迟计算/运行。
  • 我前面写的promise应用的那篇里,就发现很多人就喜欢把promise做一个function包起来返回,因为new一个promise时候会立即执行它,这样就能想什么时候调就什么时候调,而且代码看着也舒服。
  • 柯里化的例子:
function add(x) {
    return function(y) {
        return x + y;
    }
}
 
add(2)(3);  // 5
let p = ()=>(new Promise(function(resolve,reject){...}))
p.then(xxx,yyy);
  • 这2个例子我个人觉得已经很能代表柯里化思想了,再看个例子:
const checktype=(type)=>{
    return (content)=>(typeof content === type?true:false)
}
const typeli=['string','boolean','undefined','object','number']
let mymethod={};
typeli.forEach(element => {
    mymethod['is'+element]=checktype(element)
});
console.log(mymethod.isstring('123'))
console.log(mymethod.isstring(123))
  • 这个其实没多复杂,主要思路比较值得学习。
  • 分离函数类型与方法,包装成键值对的形式进行调用。checktype函数中第一次传type用来生成对象的值,第二次传参用来调用函数。
  • 调用不必输入额外参数,比如没做好的函数用户就可能输入judge(123,‘string’)这样很容易输错字符串名导致错误。
  • 下面看另一个例子,这个例子思路也比较重要。
  • 有这样一个需求,比如add函数,就是把参数给加起来,正常情况,是add(1,2),这样就会把1和2加起来,那么如果add(1)(2)(3,4,5)这种不定长度,括号后面还跟着个括号怎么实现?
  • 一般函数没有return返回值就是undefined,那么这种括号连写的,必然返回值是个function,接收个参数为后面那个参数。
let maxstack=5;//递归次数,跟所有括号的总参数个数有关
const add= (args)=>{
    let sum=0
    args.forEach((ele)=>sum=sum+ele);
    return sum
}
const curring=(fn,arr=[])=>{
    return (...args)=>{
        arr = arr.concat(args);//合并数组
        console.log(arr)
        if(arr.length<maxstack){
            return curring(fn,arr)
        }

        return fn(arr)
    }
}
console.log(curring(add)(1)(2,3)(2,213))
  • 这个思路一般有2种,一种是把参数收集起来,最后一次头调用,一种是收到一个括号参数立即加一下,然后求的和继续传给下一个函数。
  • 这里有个问题就是由于传递参数的括号不固定,所以递归次数难以测定,一般情况add函数里不使用…args然后curring里做递归出口来实现次数,或者就直接声明个次数,然后递归到小于参数时就返回值,没递归到参数个数就就继续递归。所以如果用立即加一下返回给下一个括号调用反而有点不好操作,不能判断递归次数。
  • 手写Promise中里内部有2个递归跟这个很像。
  • 柯里化还有个三次调用后执行的例子:
const after=(times,fn)=>{
    return ()=>{
        times--;
        if(times===0){
            fn()
        }
    }
 
}
const fn = ()=>{console.log('111')}
const myfunc = after(3,fn);
myfunc();
myfunc();
myfunc();
  • 这个函数主要就是在after上返回也是个函数,如果返回不是函数,那么times就没法形成闭包,不能在后面想调用就调用,这种操作还可以很好的复用。
这篇先写这么多,还有些异步小例子下篇说。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

业火之理

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值