microtasks、macrotasks与函数柯里化
1、microtasks、macrotasks
JavaScript是单线程执行的,而现在推行的多线程执行,都可以理解为伪多线程,因为所有的执行都会回归的主线程执行,
而主线程外会有如多个事件队列,等待主线程的空闲后进入执行。
而microtasks与macrotasks就是很好理解事件环的概念,然后它在不同的浏览器的执行顺序可能不一致,但不妨去理解它。
V8实现中,两个队列各包含不同的任务:
macrotasks(宏任务): script(整体代码),setTimeout, setInterval, setImmediate, I/O, UI rendering
microtasks(微任务): process.nextTick, Promises, Object.observe, MutationObserver
以上为事件环的执行图解,microtasks与macrotasks的执行过程。
在现在浏览器中可能使用到就script(整体代码),setTimeout,setInterval,UI rendering,Promises等,而其他可能不常用,如果是nodeJs可能就常用。
从资料中可以知道microtasks这个是会阻塞浏览器渲染的,因为它是在主线程一空闲下来就执行,而macrotasks就是在浏览器渲染后再执行,可以利用
这一特性有效的提高性能,如vue里的nextTick就是触发microtasks,那么就可以实现多线程执行且浏览器渲染阻塞等待处理后执行来提高性能,而不是
每次的更新都要执行渲染,那样会很消耗性能,但microtasks的执行会比事件冒泡都要优先,所以这里就会出现事件触发后当前事件执行了,后就执行
microtasks里的,然后再执行冒泡,所以还要把事件强制为macrotasks,而vue就是这样的处理逻辑。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> #a{ width: 200px; height: 200px; background: #000; } #b{ width: 100px; height: 100px; background: #ff7600; } </style> </head> <body> <div id="a"> <div id="b"></div> </div> <script> var a = document.querySelector("#a"); var b = document.querySelector("#b"); a.onclick = function(){ console.log("a"); } b.onclick = function(){ pp(); console.log("b"); } function pp(){ setTimeout(function(){ console.log("timeout"); },0); new Promise(function(resolve){ console.log("Promise"); resolve(); }).then(function(){ console.log("then"); }); } </script> </body> </html> 结果: Promise b then a timeout
从上例子可知,microtasks真的厉害。。。。也证实的以上概念。
javascript是一门单线程语言,不管是什么新框架新语法糖实现的所谓异步,其实都是用同步的方法去模拟的,牢牢把握住单线程这点非常重要。
事件循环是js实现异步的一种方法,也是js的执行机制。
2、函数柯里化
把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
这增加了函数的适用性,但同时也降低了函数的适用范围。
函数柯里化工具方法实现:
一:
function curry(){ var args = [].slice.call(arguments); var fnLen = 0; var fn = args[0]; if(typeof fn !== 'function'){ console.error('first param is not function!'); return null; } fnLen = fn.length; args.shift(); if(args[0] instanceof Array){ args = args[0]; } return function(){ var _args = [].slice.call(arguments); [].push.apply(args,_args); if(args.length >= fnLen){ return fn.apply(this, args); } return arguments.callee } } // 例子一 function fb(a,b,c,d,e,f,g){ console.log([].slice.call(arguments)); } fb = curry(fb,[10,20,30,40]); console.log(fb(50)(60)(70)); fb = curry(fb); console.log(fb(10)(20)(30)(40)(50)(60)(70)); // 例子二 function fss(v,b,n,m,x){ console.log([].slice.call(arguments)); } fss = curry(fss,107,270,307,407); console.log(fss(50));
此方法为只有被柯里化的函数的参数全部传递后才能执行函数,而参数传递完后还调用就会报错。
二:
function curry(){ var args = [].slice.call(arguments); var fn = args[0]; if(typeof fn !== 'function'){ console.error('first param is not function!'); return null; } args.shift(); if(args[0] instanceof Array){ args = args[0]; } return function(){ var _args = [].slice.call(arguments); [].push.apply(args,_args); arguments.callee.valueOf = function(){ return fn.apply(this, args); } return arguments.callee; } } function fss(v,b,n,m,x){ console.log([].slice.call(arguments)); } fss = curry(fss,107,270,307,407); console.log(fss(50)(30)(100)(1000));
这种柯里化使用的隐形转换toString,valueOf等实现,因为转换在函数调用的最后一步执行,此可以实现无限传参,无参数长度的限制。
柯里化是为了让函数的自由度增大,但也牺牲了一些性能,但在一些封装里会让代码更加顺畅,且优雅,因为一些重复代码可以省略。