/** * Defer a task to execute it asynchronously. */ var nextTick = (function () { // 闭包 内部变量 var callbacks = []; // 执行队列 var pending = false; // 标识,用以判断在某个事件循环中是否为第一次加入,第一次加入的时候才触发异步执行的队列挂载 var timerFunc; // 以何种方法执行挂载异步执行队列,这里假设Promise是完全支持的
functionnextTickHandler () { // 异步挂载的执行任务,触发时就已经正式准备开始执行异步任务了 pending = false; // 标识置false var copies = callbacks.slice(0); // 创建副本 callbacks.length = 0; // 执行队列置空 for (var i = 0; i < copies.length; i++) { copies[i](); // 执行 } }
// the nextTick behavior leverages the microtask queue, which can be accessed // via either native Promise.then or MutationObserver. // MutationObserver has wider support, however it is seriously bugged in // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It // completely stops working after triggering a few times... so, if native // Promise is available, we will use it: /* istanbul ignore if */ if (typeofPromise !== 'undefined' && isNative(Promise)) { var p = Promise.resolve(); var logError = function (err) { console.error(err); }; timerFunc = function () { p.then(nextTickHandler).catch(logError); // 挂载异步任务队列 // in problematic UIWebViews, Promise.then doesn't completely break, but // it can get stuck in a weird state where callbacks are pushed into the // microtask queue but the queue isn't being flushed, until the browser // needs to do some other work, e.g. handle a timer. Therefore we can // "force" the microtask queue to be flushed by adding an empty timer. if (isIOS) { setTimeout(noop); } }; } elseif (typeofMutationObserver !== 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { // use MutationObserver where native Promise is not available, // e.g. PhantomJS IE11, iOS7, Android 4.4 var counter = 1; var observer = newMutationObserver(nextTickHandler); var textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true }); timerFunc = function () { counter = (counter + 1) % 2; textNode.data = String(counter); }; } else { // fallback to setTimeout /* istanbul ignore next */ timerFunc = function () { setTimeout(nextTickHandler, 0); }; }