源碼裏是這麼描述的:html
A service that helps you run functions asynchronously, and use their return values (or exceptions) when they are done processing.android
大概意思是幫助你異步執行方法,且當他們執行完後可使用他們的返回值。git
This is an implementation of promises/deferred objects inspired by Kris Kowal's Q.es6
這是一個 promises/deferred 對象的實現,靈感來自於 Kris Kowal's Qgithub
下面的例子假設$q和asyncGreet在當前做用域內是有效的.express
function asyncGreet(name) { // perform some asynchronous operation, resolve or reject the promise when appropriate. return $q(function(resolve, reject) { setTimeout(function() { if (okToGreet(name)) { resolve('Hello, ' + name + '!'); } else { reject('Greeting ' + name + ' is not allowed.'); } }, 1000); }); } var promise = asyncGreet('Robin Hood'); //then函數放入pending數組 promise.then(function(greeting) { alert('Success: ' + greeting); }, function(reason) { alert('Failed: ' + reason); });
下面咱們深刻源碼內部去一探究竟:api
function $QProvider() { this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { return qFactory(function(callback) { $rootScope.$evalAsync(callback); }, $exceptionHandler); }]; } $evalAsync: function(expr, locals) { // if we are outside of an $digest loop and this is the first time we are scheduling async // task also schedule async auto-flush //若是當前不處於$digest或者$apply的過程當中(只有在$apply和$digest方法中才會設置$$phase這個字段),而且asyncQueue數組中還不存在任務時, //就會異步調度一輪digest循環來確保asyncQueue數組中的表達式會被執行 if (!$rootScope.$$phase && !asyncQueue.length) { $browser.defer(function() { //最終調用的是setTimeout if (asyncQueue.length) { $rootScope.$digest();//執行消化功能 } }); } asyncQueue.push({scope: this, expression: $parse(expr), locals: locals}); }
由以前 cacheFactory的分析
,再結合上面源碼咱們就知道 注入$q時調用了qFactory工廠方法:數組
function qFactory(nextTick, exceptionHandler) { function Promise() { //初始化Promise的狀態對象 this.$$state = { status: 0 }; } //擴展Promise類原型 extend(Promise.prototype, { //then主要是把一個defer對象和fullfiled reject 函數 放入pending數組 then: function(onFulfilled, onRejected, progressBack) { if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) { return this; } var result = new Deferred(); this.$$state.pending = this.$$state.pending || []; //把一個新的 Defer 對象push進pending數組 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]); if (this.$$state.status > 0) scheduleProcessQueue(this.$$state); //返回這個新建Defer對象的promise //能夠造成promise chain return result.promise; } //代碼省略 } //經過$q注入時返回Q函數 //隨後Q函數 傳入resolver參數調用 //調用resolver函數時傳入包裝deferred對象的resolve和 reject函數 //隨後返回promise對象 //promise對象調用then函數時放入pending隊列 var $Q = function Q(resolver) { if (!isFunction(resolver)) { throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver); } //構造一個Deferred對象 var deferred = new Deferred(); function resolveFn(value) { deferred.resolve(value); } function rejectFn(reason) { deferred.reject(reason); } //調用resolver參數函數 //resolveFn供外部調用 resolver(resolveFn, rejectFn); //返回一個promise對象 //供調用then函數 return deferred.promise; }; // Let's make the instanceof operator work for promises, so that // `new $q(fn) instanceof $q` would evaluate to true. //使得new $q(fn) 也能夠調用promise的方法 $Q.prototype = Promise.prototype; //暴露內部方法 $Q.defer = defer; $Q.reject = reject; $Q.when = when; $Q.resolve = resolve; //$q.all是用於執行多個異步任務進行回調,它能夠接受一個promise的數組, //或是promise的hash(object)。任何一個promise失敗,都會致使整個任務的失敗。 //https://blog.csdn.net/shidaping/article/details/52398925 $Q.all = all; //$q.race() 是 Angular 裏面的一個新方法,和 $q.all() 相似,可是它只會返回第一個處理完成的 Promise 給你///。假定 API 調用 1 和 API 調用 2 同時執行,而 API 調用 2 在 API 調用 1 以前處理完成,那麼你就只會獲得 //API 調用 2 的返回對象。換句話說,最快(處理完成)的 Promise 會贏得返回對象的機會: $Q.race = race; return $Q; }
調用then方法時其實是新建一個defer對象放入pending數組,在調用defer.resolve的時候
去調度這個數組中的元素,也就是任務.promise
extend(Deferred.prototype, { resolve: function(val) { //第一次resolve的時候爲0 因此會往下走 if (this.promise.$$state.status) return; if (val === this.promise) { this.$$reject($qMinErr( 'qcycle', "Expected promise to be resolved with value other than itself '{0}'", val)); } else { //調度pending數組裏的任務 this.$$resolve(val); } }, $$resolve: function(val) { var then; var that = this; var done = false; try { if ((isObject(val) || isFunction(val))) then = val && val.then; if (isFunction(then)) { //val.then方法 val是promise的時候 //resolvePromise函數裏放入了當前defer對象 this.promise.$$state.status = -1; then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify)); } else { //更新promise的狀態對象 this.promise.$$state.value = val; this.promise.$$state.status = 1; //調度的時候pending爲空就返回了 scheduleProcessQueue(this.promise.$$state); } } catch (e) { rejectPromise(e); exceptionHandler(e); } function resolvePromise(val) { if (done) return; done = true; that.$$resolve(val); } function rejectPromise(val) { if (done) return; done = true; that.$$reject(val); } } }
//傳入promise的state對象 function scheduleProcessQueue(state) { if (state.processScheduled || !state.pending) return; state.processScheduled = true; //nextTick裏調用$browser.defer函數 nextTick(function() { processQueue(state); }); }
實際最終處理的仍是processQueue函數,裏面循環調用pending數組微信
function processQueue(state) { var fn, deferred, pending; pending = state.pending; state.processScheduled = false; state.pending = undefined; for (var i = 0, ii = pending.length; i < ii; ++i) { //獲取pending數組的元素 元素自己也是數組 deferred = pending[i][0]; fn = pending[i][state.status]; try { if (isFunction(fn)) { deferred.resolve(fn(state.value)); } else if (state.status === 1) { deferred.resolve(state.value); } else { deferred.reject(state.value); } } catch (e) { deferred.reject(e); exceptionHandler(e); } } }
歡迎關注個人微信公衆號,獲取最新源碼解析文章!
參考資料: