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);
}
}
}

欢迎关注我的微信公众号,获取最新源码解析文章!

参考资料:

  1. a tiny implementation of Promises/A+.
  2. Angular中的$q的形象解释及深入用法
  3. 关于 Angular 里的 $q 和 Promise
  4. 理解状态机

angular源码剖析之Provider系列--QProvider的更多相关文章

  1. angular源码剖析之Provider系列--CacheFactoryProvider

    CacheFactoryProvider 简介 源码里是这么描述的: Factory that constructs {@link $cacheFactory.Cache Cache} objects ...

  2. angular源码阅读,依赖注入的原理:injector,provider,module之间的关系。

    最开始使用angular的时候,总是觉得它的依赖注入方式非常神奇. 如果你跳槽的时候对新公司说,我曾经使用过angular,那他们肯定会问你angular的依赖注入原理是什么? 这篇博客其实是angu ...

  3. 【安卓网络请求开源框架Volley源码解析系列】定制自己的Request请求及Volley框架源码剖析

    通过前面的学习我们已经掌握了Volley的基本用法,没看过的建议大家先去阅读我的博文[安卓网络请求开源框架Volley源码解析系列]初识Volley及其基本用法.如StringRequest用来请求一 ...

  4. 【java集合框架源码剖析系列】java源码剖析之TreeSet

    本博客将从源码的角度带领大家学习TreeSet相关的知识. 一TreeSet类的定义: public class TreeSet<E> extends AbstractSet<E&g ...

  5. 【java集合框架源码剖析系列】java源码剖析之HashSet

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于HashSet的知识. 一HashSet的定义: public class HashSet&l ...

  6. 【java集合框架源码剖析系列】java源码剖析之TreeMap

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于TreeMap的知识. 一TreeMap的定义: public class TreeMap&l ...

  7. 【java集合框架源码剖析系列】java源码剖析之ArrayList

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本. 本博客将从源码角度带领大家学习关于ArrayList的知识. 一ArrayList类的定义: public class Arr ...

  8. 【java集合框架源码剖析系列】java源码剖析之LinkedList

    注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本. 在实际项目中LinkedList也是使用频率非常高的一种集合,本博客将从源码角度带领大家学习关于LinkedList的知识. ...

  9. 【java集合框架源码剖析系列】java源码剖析之HashMap

    前言:之所以打算写java集合框架源码剖析系列博客是因为自己反思了一下阿里内推一面的失败(估计没过,因为写此博客已距阿里巴巴一面一个星期),当时面试完之后感觉自己回答的挺好的,而且据面试官最后说的这几 ...

随机推荐

  1. Bean property XX&#39; is not writable or has an invalid setter method

    刚刚搞spring.property注入时遇到这个问题,百度一下.非常多人说是命名或者get set方法不一致的问题,可是这个我是知道的.写的时候也注意到这些.所以应该不是这个问题.以为是xml头写的 ...

  2. WPF MVVM UI分离之《交互与数据分离》 基础才是重中之重~delegate里的Invoke和BeginInvoke 将不确定变为确定系列~目录(“机器最能证明一切”) 爱上MVC3系列~全局异常处理与异常日志 基础才是重中之重~lock和monitor的区别 将不确定变成确定~我想监视我的对象,如果是某个值,就叫另一些方法自动运行 将不确定变成确定~LINQ DBML模型可以对

    WPF MVVM UI分离之<交互与数据分离>   在我们使用WPF过程中,不可避免并且超级喜欢使用MVVM框架. 那么,使用MVVM的出发点是视觉与业务逻辑分离,即UI与数据分离 诸如下 ...

  3. async & await 的前世今生(Updated)----代码demo

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  4. uva 10806 Dijkstra, Dijkstra. (最小费最大流)

    uva 10806 Dijkstra, Dijkstra. 题目大意:你和你的伙伴想要越狱.你的伙伴先去探路,等你的伙伴到火车站后,他会打电话给你(电话是藏在蛋糕里带进来的),然后你就能够跑去火车站了 ...

  5. java的多态以及重载,重写,前期绑定,后期绑定

    多态的定义: 一个类实例的相同方法在不同情形有不同表现形式.多态机制使具有不同内部结构的对象可以共享相同的外部接口.这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通 ...

  6. linux 命令之 watch

    watch能够帮你监測一个命令的执行结果,省得你一遍遍的手动执行.在Linux下.watch是周期性的执行下个程序.并全屏显示执行结果.你能够拿他来监測你想要的一切命令的结果变化,比方 tail 一个 ...

  7. A new session could not be created. (Original error: Requested a new session but one was in progress) )错误解决办法

    z在desiredCapabilities里新增这俩居然fix了问题,原因暂时不得而知: capabilities.setCapability("unicodeKeyboard", ...

  8. Python调用C/Fortran混合的动态链接库--上篇

    内容描述: 在32位或64位的windows或GNU/Linux系统下利用Python的ctypes和numpy模块调用C/Fortran混合编程的有限元数值计算程序 操作系统及编译环境: 32bit ...

  9. collection 模块 双端队列

    单端队列 用于同一进程中的队列,可以叫做单进程队列. queue 遵循先进先出,先进去的必须先出来 1.先进先出: impore queue q = queue.Queue() 实例化一个对象 q.p ...

  10. SEO搜索引擎基础原理