You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
import{isNative}from'./typeutil'import{log}from'./debug'constcallbacks=[]letpending=falsefunctionflushCallbacks(){pending=falseconstcopies=callbacks.slice(0)callbacks.length=0for(leti=0;i<copies.length;i++){copies[i]()}}// Here we have async deferring wrappers using both micro and macro tasks.// In < 2.4 we used micro tasks everywhere, but there are some scenarios where// micro tasks have too high a priority and fires in between supposedly// sequential events (e.g. #4521, #6690) or even between bubbling of the same// event (#6566). However, using macro tasks everywhere also has subtle problems// when state is changed right before repaint (e.g. #6813, out-in transitions).// Here we use micro task by default, but expose a way to force macro task when// needed (e.g. in event handlers attached by v-on).letmicroTimerFuncletmacroTimerFuncletuseMacroTask=false// Determine (macro) Task defer implementation.// Technically setImmediate should be the ideal choice, but it's only available// in IE. The only polyfill that consistently queues the callback after all DOM// events triggered in the same loop is by using MessageChannel./* istanbul ignore if */if(typeofsetImmediate!=='undefined'&&isNative(setImmediate)){macroTimerFunc=()=>{setImmediate(flushCallbacks)}}elseif(typeofMessageChannel!=='undefined'&&(isNative(MessageChannel)||// PhantomJSMessageChannel.toString()==='[object MessageChannelConstructor]')){constchannel=newMessageChannel()constport=channel.port2channel.port1.onmessage=flushCallbacksmacroTimerFunc=()=>{port.postMessage(1)}}else{/* istanbul ignore next */macroTimerFunc=()=>{setTimeout(flushCallbacks,0)}}// Determine MicroTask defer implementation./* istanbul ignore next, $flow-disable-line */if((typeofPromise!=='undefined')&&isNative(Promise)){constp=Promise.resolve()microTimerFunc=()=>{p.then(flushCallbacks)}}else{// fallback to macromicroTimerFunc=macroTimerFunc}/** * Wrap a function so that if any code inside triggers state change, * the changes are queued using a Task instead of a MicroTask. */exportfunctionwithMacroTask(fn: Function): Function{returnfn._withTask||(fn._withTask=function(){useMacroTask=trueconstres=fn.apply(null,arguments)useMacroTask=falsereturnres})}exportfunctionnextTick(cb?: Function,ctx?: Object){log('nextTick',cb)let_resolvecallbacks.push(()=>{if(cb){try{cb.call(ctx)}catch(e){console.log('nextTick',e)}}elseif(_resolve){_resolve(ctx)}})if(!pending){
pending =trueif(useMacroTask){macroTimerFunc()}else{microTimerFunc()}}// $flow-disable-lineif(!cb&&typeofPromise!=='undefined'){returnnewPromise(resolve=>{_resolve=resolve})}}
The text was updated successfully, but these errors were encountered:
参考通过microtasks和macrotasks看JavaScript异步任务执行顺序 和 【朴灵评注】JavaScript 运行机制详解:再谈Event Loop 和 vue的源码next-tick.js。
macrotasks和microtasks的划分:
macrotasks:
microtasks:
处理机制
通俗的解释一下,microtasks的作用是用来调度应在当前执行的脚本执行结束后立即执行的任务。 例如响应事件、或者异步操作,以避免付出额外的一个task的费用。
microtask会在两种情况下执行:
任务队列(macrotask = task queue)回调后执行,前提条件是当前没有其他执行中的代码。
每个task末尾执行。
另外在处理microtask期间,如果有新添加的microtasks,也会被添加到队列的末尾并执行。
也就是说执行顺序是:
开始 -> ** 取task queue第一个task执行 -> 取microtask全部任务依次执行 -> 取task queue下一个任务执行 -> 再次取出microtask全部任务执行 ->** … 这样循环往复
以上文本都引自上面的文章。
之前的测试代码问题
之前的代码里面,更新了三次属性,那么就会render三次,使用了event loop的异步队列之后,只会更新一次。
实现
wathcer 类update的时候,放入队列。
队列实现
nextTick实现
代码来自 vue
使用的队列是 微任务队列
MicroTask
,实现是用Promise.resolve()
的回调函数会加入 microtask 来实现的。microtask的优先级会更加高(准确的说更加快的执行)。setImmediate 是ie才有的。MessageChannel是h5有的,这2个的任务都只能放到 `macrotask‘ (大任务队列?),只有 Promise的才能放到 microtask 里面。
如果不是h5,那只能用 setTimeout(flushCallbacks, 0) 了。默认有4毫秒延迟,而且是异步的。
The text was updated successfully, but these errors were encountered: