`

轻松使用$q进行异步编程

 
阅读更多
第一部分关于js中的异步编程

  异步编程简单的说就是你写了一段代码,但他不会按照你书写代码的顺序立即执行,而是等到程序中发生了某个事件(如用户点击了某个按钮,某个ajax请求得到了响应)才去执行这段代码,而且这段代码可能执行一次(如一个ajax请求得到了响应)、也可能执行很多次或者不执行(一个按钮被点击了许多次或者0次)这就是所谓的异步编程。

  有两种异步程序模式单次执行模式和监听执行模式。像ajax请求这样的就是属于单次执行模式,请求、回调只会进行一次。像事件绑定就属于监听执行模式,只要事件发生回调就可以不断的执行。但是不管是单次执行模式还是监听执行模式在js程序中的共同点都是保存着一个函数引用(这个引用的表现形式就回调函数),在某个事件发生后开始执行这个函数引用中的代码。
        异步执行体:包含异步执行代码(或者异步动作)和状态,状态不可逆。

  回调执行体:包含回调执行代码和状态,状态不可逆。

  两种执行体是依靠异步执行体发送信号通信。

  监听执行模式和单次执行模式的区别:监听模式中会有多个异步执行体和回调执行体副本,且每个异步执行体副本关联一个回调执行体副本。多个副本的这两种执行体的执行代码是一样的,唯一区别的就是每个副本的状态不同。

  代码举例:
//单次执行模式:
funtion a(b) {
       console.log(“a process”);
       b();
};
function b() {
       console.log(“b process”);
};
a(b); //发送信号
//监听执行模式(这里直接使用jquery):
$(“#button”).bind(“click”, function() {
       console.log(“click callback”);
});
$(“#button”).trigger(“click”); //发送信号


第二部分关于promise模式的异步编程

  上面就是异步编程的两种形式,由于我们的$q是解决第一种模式中存在的问题的,所以这里就讨论讨论单次执行模式中存在的问题。你可以设想这么一种场景,当你要在很多个ajax请求响应完成后做一件事情,你用现有的js的回调方式,会不会发现回调的层次很深、代码十分混乱。而然我们promise模式的异步编程方式就能很好的管理这些单次执行模式下的代码,使你的代码书写起来更加优雅,说白了promise不产生新的东西只是一个语法糖使你编写出更加优雅的异步程序代码。

  那promise模式到底是什么样的呢,说白了就是把把我上面的单次执行模式图抽象化了,用一个defer对象(延期对象)代表异步执行体,用一个promise对象(承诺对象)代表回调执行体,这个defer对象可以发送消息,promise接受消息,然后执行相应的回调函数。Promise就是异步执行体和回调执行体之间的桥梁,这样的好处是异步执行体和回调执行体这种模型很好的对异步动作和回调动作进行了解耦。你可以在promise上面好好的操纵你的回调执行体,而只是接受一个来自defer发送的参数。

  这里还不能很好的体现出promise模式的优势,而她真正的优势是在这种模型下扩充的api使其发挥了真正的强大作用。比如说promise对象的then方法,这个方法就是支持异步链式编程最重要的方法,他也是使代码更加优雅最重要的方法。还有比如说all接收一个promise数组返回一个新的promise,当前面的所有promise状态都完成之后这个新的promise才能完成,这个很适合多个ajax后处理某些事情。不过可能你还不能明白,下面在介绍$qAPI的时候有详细的介绍。

第三部分 $q服务的API详解

  下面我们通过讲解$q的API让你更多的了解promise异步编程模式。$q是做为angularjs的一个服务而存在的,只是对promise异步编程模式的一个简化实现版,源码中剔除注释实现代码也就二百多行,下面开始介绍$q的API。

     defer对象(延迟对象)可以通$q.defer()获取,下面是defer对象的api:

     方法:

     resolve(value):向promise对象异步执行体发送消息告诉他我已经成功完成任务,value即为发送的消息。

     reject(value): 向promise对象异步执行体发送消息告诉他我已经不可能完成这个任务了,value即为发送的消息。

     notify(value): 向promise对象异步执行体发送消息告诉他我现在任务完成的情况,value即为发送的消息。

  这些消息发送完promise会调用现有的回调函数。

   属性:

   promise即与这个defer对象的承诺对象。

  从上可以看出defer主要是用来发送消息的。

  promise对象可以通过defer.promise获取,下面是promise对象的api:

  方法:

  then(successCallback,errorCallback,notifyCallback):参数为不同消息下的不同回调函数,defer发送不同的消息执行不同的回调函数,消息作为这些回调函数的参数传递。返回值为回一个promise对象为支持链式调用而存在。当第一个defer对象发送消息后,后面的promise对应的defer对象也会发送消息,但是发送的消息不一样,不管第一个defer对象发送的是reject还是resolve,第二个及其以后的都是发送的resolve,消息是可传递的。

  catch(errorCallback):then(null,errorCallback)的缩写。

  finally(callback):相当于then(callback,callback)的缩写,这个finally中的方法不接受参数,却可以将defer发送的消息和消息类型成功传递到下一个then中。

  代码举例:
function f1(num) {
       document.write("success:" + (num++) + "<br/>");
       return num;
}
function f2(num) {
       document.write("errror:" + (num++) + "<br/>");
       return num;
}
var defer = $q.defer();
var promise = defer.promise;
//方式1
// promise.then(f1,f2).then(f1,f2);
// 方式2
// promise.then(f1,f2);
// promise.then(f1,f2);
// 方式3
// promise.then(f1,f2).then(f1,f2);
// promise.catch(f2);
// promise.finally(f2);
//方式4
// promise.finally(f2).then(f1,f2);
defer.reject(1);
方式1的结果:
errror: 1
success: 2
方式2的结果:
errror: 1
errror: 1
方式3的结果:
errror: 1
errror: 1
error: NaN
success: 2
方式4的结果:
Error: NaN
Error: 1

现在继续$q的api:

  方法:

  defer():用来生成一个延迟对象 var defer =$q.defer();

  reject():参数接收错误消息,相当于在回调函数中抛出一个异常,然后在下一个then中调用错误的回调函数。

  代码举例:
var defer = $q.defer();
var promise = defer.promise;
promise.then(function() {
       return $q.reject("success error");
}, function() {
       return $q.reject("error error");
}).then(function(info) {
       document.write("s:" + info + "<br/>");
}, function(info) {
       document.write("e:" + info + "<br/>");
});
//方式1
// defer.reject(1);
//方式2
// defer.resolve(1);
方式1运行结果
e: error error
方式2运行结果
e: success error

  all():参数接收为一个promise数组,返回一个新的单一promise对象,当这些promise对象对应defer对象全部解决这个单一promise对象才会解决,当这些promise对象中有一个被reject了,这个单一promise同样的被reject了。

  代码举例:
var defer1 = $q.defer();
var promise1 = defer1.promise;
promise1.then(function(num) {
              console.log("success" + num);
       },
       function(num) {
              console.log("error" + num);
       });
var defer2 = $q.defer();
var promise2 = defer2.promise;
promise1.then(function(num) {
              console.log("success" + num);
       },
       function(num) {
              console.log("error" + num);
       });
var promise3 = $q.all([promise1, promise1]);
promise3.then(function(num) {
       console.log("s:" + num);
}, function(num) {
       console.log("e:" + num);
});
//方式1
// defer1.resolve(1);
// defer2.resolve(1);
//方式2 
//defer1.reject(1);
方式1的结果:
success1
success2: 1
s: 1,
1
方式2的结果:
error1
e: 1

  when():接收第一个参数为一个任意值或者是一个promise对象,其他3个同promise的then方法,返回值为一个promise对象。第一个参数若不是promise对象则直接运行success回调且消息为这个对象,若为promise那么返回的promise其实就是对这个promise类型的参数的一个包装而已,被传入的这个promise对应的defer发送的消息,会被我们when函数返回的promise对象所接收到。

  代码举例:
var promise = $q.when(1, function(num) {
       console.log("s" + num);
}, function() {
       console.log("e");
});
var defer1 = $q.defer();
var promise1 = defer1.promise;
var promise2 = $q.when(promise1, function(num) {
       console.log("s" + num);
}, function() {
       console.log("e");
});
defer1.reject(1);
运行结果:
s1
e

  对上面还有一个注意事项就是defer对象发送消息不会立即执行的,而是把要执行的代码放到了rootScope的evalAsync队列当中,当时scope.$apply的时候才会被promise接收到这个消息。

  到这里$q服务全部介绍完了,对于angular中的then的链式调用中如果defer发送的reject的那么只有第一个promise是reject的回调,其他的都是resolve的回调这里多少觉得是不合适的,不知道是个bug还是就是这样设计?$q只适合单次执行模式,不知道是否适合扩展成监听执行模式?这都是大家值得思考的问题。
分享到:
评论

相关推荐

    STC-IS0-15XX-V6.86Q

    使用STC-IS0-15XX-V6.86Q,开发者可以轻松地完成以下步骤: 1. 安装USB驱动:首先,根据“STC-USB驱动安装说明.pdf”中的指示安装USB转UART驱动和STC-USB驱动。 2. 连接设备:通过USB线将STC单片机开发板与电脑连接...

    前端开源库-nodeunitq

    `nodeunitq`的目标是使那些使用`Q`进行异步编程的开发者能够在`nodeunit`中更方便地编写测试用例。通过将`Q`的Promise概念与`nodeunit`的测试结构相结合,`nodeunitq`能够帮助开发者有效地测试异步流程,避免回调...

    angular-1.4.0-rc

    - **服务**:AngularJS提供了一系列内置服务(如$http用于HTTP请求,$q用于异步编程)以及自定义服务的能力,方便模块化开发。 **2. AngularJS 1.4.0-rc的改进与新特性** 在1.4.0-rc版本中,可能会包含一些新的...

    C#以太网三菱Q系列PLC通讯程式

    在本文中,我们将深入探讨如何使用C#进行以太网TCP通信,特别是在与三菱Q系列PLC进行数据交互时的程序实现。C#是一种广泛应用于Windows平台的编程语言,尤其适用于开发工业自动化和控制系统的应用。三菱Q系列PLC是...

    AngularJs+Bootstrap前端框架

    4. **服务**:AngularJS 的服务是可重用的功能模块,如 $resource 用于与 RESTful API 交互,$q 用于异步编程,$timeout 和 $interval 分别用于延迟和周期性执行函数。 5. **模块化**:AngularJS 应用由多个模块...

    AngularJs 1.5.8

    7. **服务**:AngularJS 1.5.8提供了多种内置服务,如$http用于处理HTTP请求,$q用于异步编程,$rootScope是所有控制器共享的根作用域等。这些服务极大地丰富了框架的功能。 8. **过滤器**:过滤器用于格式化和转换...

    XE5QB移动开发综合案例源码

    8. **性能优化**:移动设备资源有限,源码可能包含性能优化技巧,如内存管理、异步编程、后台处理等,这些都是移动开发中的重要课题。 9. **调试和测试**:源码可能包含了调试和测试的策略,比如使用XE5内置的调试...

    QT异步命令行,通过信号和槽进行响应

    以下是一个简单的示例,展示了如何使用信号和槽来异步执行命令行操作: ```cpp #include #include #include class CmdRunner : public QObject { Q_OBJECT public: explicit CmdRunner(QObject *parent = ...

    angularJs权威和精通angularjs

    服务是AngularJS中的单例对象,它们负责执行特定任务,如$http用于与服务器通信,$location用于处理URL,$q用于异步编程等。 **7. Routing:** AngularJS的路由功能使得在单页面应用中导航变得简单,通过uirouter或...

    node.js中使用q.js实现api的promise化

    以上方法展示了如何使用Q.js将Node.js中的传统回调风格API转换为Promise风格API,从而提供更加清晰、易于维护的异步编程解决方案。通过实践这些方法,开发者可以轻松地将现有的异步代码库迁移到Promise模型上,改善...

    易语言源码易语言仿Q-dir资源管理器源码.rar

    易语言是一种专为中国人设计的编程语言,它以简体中文作为编程语言,降低了编程的门槛,使得更多非计算机专业的人也能轻松学习编程。这款"易语言仿Q-dir资源管理器源码"是一个基于易语言实现的项目,旨在模仿Q-dir...

    AngularJS+webapi前端项目

    4. **服务**:AngularJS提供了一系列内置服务,如$http用于Ajax请求,$scope用于数据绑定,$q用于异步编程等。 5. **MVC架构**:AngularJS遵循Model-View-Controller设计模式,帮助开发者组织代码结构。 **...

    stm32f407 w25q128 SPI stm32cubemx+ uart dma+timer定时功能0.zip

    在STM32CUBEMX工具的支持下,我们可以轻松配置STM32F407的SPI接口,设置其时钟极性和相位,以及模式为主模式或从模式,以满足与W25Q128通信的需求。通过SPI,可以实现快速的数据交换,例如读取或编程外部存储器的...

    qq机器人酷q源码V3.0.e

    6. **多线程编程**:由于需要同时处理多个QQ事件,酷Q可能采用了多线程或者异步IO模型,确保了在高并发情况下的稳定运行。 7. **日志记录**:为了方便调试和问题排查,源码中可能包含了日志记录功能,帮助开发者...

    前端开源库-qajax

    Qajax库的出现,为开发者提供了一个更易用、更符合Promise规范的API,使得异步编程变得更加简洁、易于理解和维护。 首先,我们要理解什么是Ajax。AJAX(Asynchronous JavaScript and XML)是一种在无需刷新整个页面...

    spdlog-master.rar_mpmc_bounded_q.h_spdlog

    标题中的"spdlog-master....通过对`spdlog`库的深入理解和使用,开发者可以轻松地在他们的C++项目中实现高效的日志记录功能,同时通过`mpmc_bounded_q.h`等内部组件,可以了解到更多关于并发编程和数据结构优化的知识。

    AngularJS-1.3

    AngularJS 1.3 对 Promise 进行了改进,使其更符合 Promises/A+ 规范,提高了异步编程的便利性。 10. **性能提升**: 1.3 版本通过优化脏检查 (`$digest`) 循环,降低了内存占用,并提高了整体性能。`$$watchers`...

    qt串口编程实例可收发

    7. **用户界面**:为了使用户能直观地看到收发数据的情况,实例可能还包含了一个简单的用户界面,比如使用QT的Q widgets来创建按钮、文本框和标签,用于显示串口状态、发送数据、接收数据以及计数器的值。...

    易语言-易语言Q号取Q名称

    易语言支持异步编程,可以使用“启动子线程”命令在后台执行任务,然后通过事件回调的方式更新界面。 最后,为了让其他开发者能够理解并复用你的代码,良好的注释和文档是必不可少的。易语言中的“注释”命令可以...

Global site tag (gtag.js) - Google Analytics