QProvider 簡介
原始碼裡是這麼描述的:
A service that helps you run functions asynchronously, and use their return values (or exceptions) when they are done processing.
大概意思是幫助你非同步執行方法,且當他們執行完後可以使用他們的返回值。
This is an implementation of promises/deferred objects inspired by Kris Kowal's Q.
這是一個 promises/deferred 物件的實現,靈感來自於 Kris Kowal's Q
QProvider 用法
下面的例子假設$q和asyncGreet在當前作用域內是有效的.
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);
});
下面我們深入原始碼內部去一探究竟:
$QProvider 定義
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工廠方法:
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的時候
去排程這個陣列中的元素,也就是任務.
resolve 方法
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);
}
}
}
scheduleProcessQueue 方法
//傳入promise的state物件
function scheduleProcessQueue(state) {
if (state.processScheduled || !state.pending) return;
state.processScheduled = true;
//nextTick裡呼叫$browser.defer函式
nextTick(function() { processQueue(state); });
}
processQueue 方法
實際最終處理的還是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);
}
}
}
歡迎關注我的微信公眾號,獲取最新原始碼解析文章!
參考資料: