在如今都追求用户体验的时代,Ajax应用真的是无所不在。加上这些年浏览器技术、HTML5以及CSS3等的发展,越来越多的富Web应用出现;在给与我们良好体验的同时,Web开发人员在背后需要处理越来越多的异步回调逻辑。

笔者对最近读完的《Async Javascript-Build More Responsive Apps with Less Code》(Javascript异步编程-设计快速响应的网络应用)一书以及部分资料,整理了我认为比较重要的一些点以及容易理解错的地方,使大家对 Promise对象以及异步编程有更深的认识。

嵌套式回调

  1. setTimeout(function() {
  2. setTimeout(function() {
  3. // do something
  4. }, 10)
  5. }, 100);
  6. $.ajax(url, function() {
  7. $.ajax(url2, function() {
  8. $.ajax(url3, function() {
  9. // do something
  10. });
  11. });
  12. });

可是问题来了,当我们的嵌套越多,代码结构层级会变得越来越深。首先是阅读上会变得困难;其次是强耦合,接口变得不好扩展。我们需要一种模式来解决这种问题,这就是Promises所要做的事情。

异步函数类型

Javascript里异步函数可以分为两大类型:

  • I/O函数(Ajax、script…)
  • 计时函数(setTimeout、setInterval、setImmediate)

异步函数异常捕获

  1. try {
  2. setTimeout(function A() {
  3. setTimeout(function B() {
  4. setTimeout(function C() {
  5. throw new Error('Error');
  6. }, 0);
  7. }, 0);
  8. }, 0);
  9. } catch (e) {}

运行以上代码,A、B、C被添加到事件队列里;异常触发时,A、B已被移出事件队列,内存堆栈里只存在C,此时的异常不被try捕获,只会流向应用程序未捕获异常处理器。

所以,在异步函数里,不能使用try/catch捕获异常。

分布式事件

Javascript的事件核心是事件分发机制,通过对发布者绑定订阅句柄来达到异步相响应的目的:

  1. document.onclick = function() {
  2. // click
  3. };

PubSub(Publish/Subscribe, 发布/订阅)模式,就是这么一种模式,通过订阅发布者的事件响应来达到多层分发解耦的目的。

以下是一个简单版本的PubSub模式实现:

  1. var PubSub = (function() {
  2. var _handlers = {};
  3. return {
  4. // 订阅事件
  5. on: function(eventType, handler) {
  6. if (!_handlers[eventType]) {
  7. _handlers[eventType] = [];
  8. }
  9. if (typeof handler == 'function') {
  10. _handlers[eventType].push(handler);
  11. }
  12. },
  13. //发布事件
  14. emit: function(eventType) {
  15. var args = Array.prototype.slice.call(arguments, 1);
  16. var handlers = _handlers[eventType] || [];
  17. for (var i = 0, len = handlers.length; i < len; i++) {
  18. handlers[i].apply(null, args)
  19. }
  20. }
  21. };
  22. })();

Promises/A规范

CommonJS之Promises/A规范是Kris Zyp于2009年提出来的,它通过规范API接口来简化异步编程,使我们的异步逻辑代码更易理解。

遵循Promises/A规范的实现我们称之为Promise对象,Promise对象有且仅有三种状态:unfulfilled(未完成)、 fulfilled(已完成)、failed(失败/拒绝);而且状态变化只能从unfulfilled到fulfilled,或者 unfulfilled到failed;

Promise对象需实现一个then接口,then(fulfilledHandler, errorHandler, progressHandler);then接口接收一个成功回调(fulfilledHandler)与一个失败回调(errorHandler);progressHandler触发回调是可选的,Promise对象没有强制去回调此句柄。

then方法的实现需要返回一个新的Promise对象,以形成链式调用,或者叫Promise管道。

为了实现状态的转变,我们还需要实现另外两个接口:

  • resolve:实现状态由未完成到已完成
  • reject:实现状态由未完成到拒绝(失败)

这样子我们开篇所说的嵌套式回调就可以这样子写了:

  1. // 这里假设Promise是一个已实现的Promise对象
  2. function asyncFn1() {
  3. var p = new Promise();
  4. setTimeout(function() {
  5. console.log(1);
  6. p.resolve(); // 标记为已完成
  7. }, 2000);
  8. return p;
  9. }
  10. function asyncFn2() {
  11. var p = new Promise();
  12. setTimeout(function() {
  13. console.log(2);
  14. p.reject('error'); // 标记为拒绝
  15. }, 1000);
  16. return p;
  17. }
  18. asyncFn1()
  19. .then(function() {
  20. return asyncFn2();
  21. }).then(function() {
  22. console.log('done');
  23. }, function(err) {
  24. console.log(err);
  25. });

有了Promise,我们可以以同步的思维去编写异步的逻辑了。在同步函数的世界里,有2个非常重要的概念:

  • 有返回值
  • 可以抛出异常

Promise不仅仅是一种可以链式调用的对象,更深层次里,它为异步函数与同步函数提供了一种更加直接的对应关系。

上面我们说过,在异步函数里,不能使用try/catch捕获异常,因此也不能抛出异常。有了Promise,只要我们显式定义了errorHandler,那么我们就可以做到像同步函数那样的异常捕获了。

转: Promises与Javascript异步编程的更多相关文章

  1. Promises与Javascript异步编程

    Promises与Javascript异步编程 转载:http://www.zawaliang.com/2013/08/399.html 在如今都追求用户体验的时代,Ajax应用真的是无所不在.加上这 ...

  2. JavaScript异步编程原理

    众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着. ...

  3. 深入解析Javascript异步编程

    这里深入探讨下Javascript的异步编程技术.(P.S. 本文较长,请准备好瓜子可乐 :D) 一. Javascript异步编程简介 至少在语言级别上,Javascript是单线程的,因此异步编程 ...

  4. 探索Javascript异步编程

    异步编程带来的问题在客户端Javascript中并不明显,但随着服务器端Javascript越来越广的被使用,大量的异步IO操作使得该问题变得明显.许多不同的方法都可以解决这个问题,本文讨论了一些方法 ...

  5. JavaScript异步编程的主要解决方案—对不起,我和你不在同一个频率上

    众所周知(这也忒夸张了吧?),Javascript通过事件驱动机制,在单线程模型下,以异步的形式来实现非阻塞的IO操作.这种模式使得JavaScript在处理事务时非常高效,但这带来了很多问题,比如异 ...

  6. javascript异步编程的前世今生,从onclick到await/async

    javascript与异步编程 为了避免资源管理等复杂性的问题, javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是为 ...

  7. JavaScript异步编程(2)- 先驱者:jsDeferred

    JavaScript当前有众多实现异步编程的方式,最为耀眼的就是ECMAScript 6规范中的Promise对象,它来自于CommonJS小组的努力:Promise/A+规范. 研究javascri ...

  8. 5分种让你了解javascript异步编程的前世今生,从onclick到await/async

      javascript与异步编程 为了避免资源管理等复杂性的问题,javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是 ...

  9. JavaScript异步编程

    前言 如果你有志于成为一个优秀的前端工程师,或是想要深入学习JavaScript,异步编程是必不可少的一个知识点,这也是区分初级,中级或高级前端的依据之一.如果你对异步编程没有太清晰的概念,那么我建议 ...

随机推荐

  1. 火星A+B(字符串整形转化,进制)

    Description 读入两个不超过25位的火星正整数A和B,计算A+B.需要注意的是:在火星上,整数不是单一进制的,第n位的进制就是第n个素数.例如:地球上的10进制数2,在火星上记为“1,0”, ...

  2. google base 之MessagePumpForUI

    base库中比较有意思就是这个类了,如同很多界面库一样,创建了一个隐藏窗口来处理需要在界面线程处理的消息,大体原理也就是需要执行task的时候发送一个自定义的消息,当窗口接收到task的时候调用保存起 ...

  3. VARIANT类型

    VARIANT的结构可以参考头文件VC98\Include\OAIDL.H中关于结构体tagVARIANT的定义.struct  tagVARIANT    {    union         {  ...

  4. 基于nginx+lua简单的灰度发布系统

    upstream.conf upstream grey_1 { keepalive 1000; server localhost:8020; } upstream grey_2 { keepalive ...

  5. QF——UI之几种常用的隐藏键盘的方法

    怎么在填写完UITextField之后,点击空白处,隐藏软键盘. 下面两个方法都可以隐藏键盘 [tf resignFirstResponder]; 停止textfield的第一响应者 [self.vi ...

  6. Android Fragment StartActivityForresult调用实例

    fragment里面的onActivityResult 怎样才能被调用,很简单,就一句话, startActivityForResult(intent, getActivity().RESULT_FI ...

  7. asp.net mvc 不找其他view模板,只找cshtml

    asp.net mvc 默认找view文件时,依次找后辍名为aspx.ascx.cshtml.vbhtml的view文件.但是项目住住用C#+Razor开发,这样找,岂不有性能损失. 添加以下代码: ...

  8. CDC不同模式在ODI体现系列之二 异步模式

    CDC不同模式在ODI体现系列之二 异步模式 2 异步模式需要在数据库中做一些准备工作: 改数据为归档并启用logminer: SQL> shutdown immediate 数据库已经关闭. ...

  9. 接收时必须库存可处理标识为Y

    应用 Oracle Inventory 层 Level Function 函数名 Funcgtion Name RCV_RCVRCERC 表单名 Form Name RCVRCERC 说明 Descr ...

  10. COB (Chip On Board) 製程介紹/簡介/注意事項 II

    銀膠 (Silver glue) 如果晶圓有接地或是散熱需求時,一般都會採用[銀膠],如果沒有的話則會採用[厭氧膠].[厭氧膠]顧名思義就是阻隔它與空氣接觸後就會自然固化,不需要高溫烘烤.使用銀膠則需 ...