Nodejs Promise 读书笔记

前言

Promise是抽象异步处理对象以及对其进行各种操作的组件。(Promise并不是从JavaScript中发源的概念)。简单说就是一个容器, 里面保存着某个未来才会结束的事件( 通常是一个异步操作)的结果。

JavaScript中是通过回调函数来处理异步逻辑的,比如读取文件的代码,如下所示

getAsync("fileA.txt", function(error, result){if(error){// 取得失败时的处理 throw error;}
}
复制代码

Nodejs中规定在Javascript的回掉函数的第一个参数是Error对象。像上面这样基于回调函数的异步处理如果统一参数使用规则的话,写法也会很明了。但是,这也仅是编码规约而已,即使采用不同的写法也不会出错。

Promise则是把类似的异步处理对象和处理规则进行规范化,并按照采用统一的接口来编写,而采取规定方法之外的写法都会出错。

下面通过Promise写法改写上面的函数

var promise = getAsyncPromise("fileA.txt"); //返回promise对象
promise.then(function(result){// 获取文件内容成功时的处理
}).catch(function(error){// 获取文件内容失败时的处理
});
复制代码

我们可以向这个预设了抽象化异步处理的promise对象,注册这个promise对象执行成功 时和失败时相应的回调函数。

这和回调函数方式相比有哪些不同之处呢? 在使用promise进行一步处理的时候,我们 必须按照接口规定的方法编写处理代码。

也就是说,除promise对象规定的方法(这里的 thencatch )以外的方法都是不可以使用的,而不会像回调函数方式那样可以自己自由的定义回调函数的参数,而必须严格遵守固定、统一的编程方式来编写代码。

这样,基于Promise的统一接口的做法, 就可以形成基于接口的各种各样的异步处理模 式。所以,promise的功能是可以将复杂的异步处理轻松地进行模式化,这也可以说得上是 使用promise的理由之一。

Promise 简介

构造器

要想创建一个Promise对象,可以使用new调用Promise的构造器来进行实例化。

var promise = new Promise(function(resolve, reject) { // 异步处理
// 处理结束后、调用resolve 或 reject
});
复制代码

实例方法

Promise.prototype.then

对通过new生成的promise对象为了设置其值在 resolve (成功)/ reject(失败)时调用的回调函数 可以使用 promise.then()实例方法(也就是说作用是为 Promise 实例添加状态改变时的回调函数。)。

promise.then(onFulfilled, onRejected)
复制代码

then方法的第一个参数是 Resolved 状态的回调函数, 第二个参数( 可选) 是 Rejected 状态的回调函数。

  • resolve(成功)时 onFulfilled 会被调用
  • reject(失败)时 onRejected 会被调用

onFulfilled 、 onRejected 两个都为可选参数。

then方法返回的是一个新的 Promise 实例( 注意,不是原来那个 Promise 实例)。 因此可以采用链式写法, 即then方法后面再调用另一个`then方法。

getJSON("/post/1.json") //返回一个Promise对象,详见下文
.then(function(post) {  return getJSON(post.commentURL);  //返回一个Promise对象
})
.then(function funcA(comments) {  console.log("Resolved: ", comments);
}, function funcB(err) {  console.log("Rejected: ", err);
});
复制代码

上面的代码使用then方法,依次指定了两个回调函数。 第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。采用链式的then,可以指定一组按照次序调用的回调函数。

Promise.prototype.catch()

promise.then成功和失败时都可以使用。另外在只想对异常进行处理时可以采用Promise.then(undefined, onRejected)这种方式,只指定reject时的回调函数即可。Promise.prototype.catch方法是.then(null, rejection) 的别名, 用于指定发生错误时的回调函数,等同于抛出错误。 上文的代码可以改造成如下

getJSON("/post/1.json") //返回一个Promise对象,详见下文
.then(function(post) {  return getJSON(post.commentURL);  //返回一个Promise对象
})
.then(function (comments) {  console.log("Resolved: ", comments);
})
.catch(err) {  console.log("Rejected: ", err);
});
复制代码

需要注意的是,如果 Promise 状态已经变成Resolved, 再抛出错误是无效的。

var promise = new Promise(function(resolve, reject) {  resolve('ok');  throw new Error('test');
});
promise  .then(function(value) {  console.log(value)  })  .catch(function(error) {  console.log(error)  });
// ok
复制代码

上面代码中, Promise 在resolve语句后面,再抛出错误,不会被捕获, 等于没有抛出。

Promise 对象的错误具有“ 冒泡” 性质, 会一直向后传递, 直到被捕获为止。 也就是说, 错误总是会被下一个catch语句捕获。

var catchTest = new Promise(function(resolve, reject) {  setTimeout(function(){resolve('aa')}, 1000)
}) catchTest
.then(function(value){console.log('a')
})
.then(function(value){throw new Error('test');  console.log('b')
})
.then(function(value){console.log('c')
})
.catch(function(error){console.log(error)
})//a
//[Error: test]
复制代码

上面代码中,一共有四个Promise 对象:一个由'catchTest'产生, 三个由then产生。它们之中的第二个then方法出了错误,中断了下面的then方法,直接被最后一个catch捕获。

建议总是使用catch方法, 而不使用then方法的第二个处理错误的参数。

跟传统的try / catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码, 即不会有任何反应。

var someAsyncThing = function() {  return new Promise(function(resolve, reject) {  //  下面一行会报错,因为 x 没有声明  resolve(x + 2);  });
};
someAsyncThing().then(function() {  console.log('everything is great');
});
复制代码

上面代码中,someAsyncThing函数产生的 Promise 对象会报错, 但是由于没有指定catch方法,这个错误不会被捕获,也不会传递到外层代码, 导致运行后没有任何输出。

注意, Chrome 浏览器不遵守这条规定, 它会抛出错误“ ReferenceError: x is not defined”。

var promise = new Promise(function(resolve, reject) {  resolve("ok");  setTimeout(function() {  throw new Error('test')  }, 0)
});
promise.then(function(value) {  console.log(value)
});
// ok
// Uncaught Error: test
复制代码

上面代码中,Promise指定在下一轮“ 事件循环” 再抛出错误, 结果由于没有指定使用try...catch语句,就冒泡到最外层,成了未捕获的错误。 因为此时,Promise 的函数体已经运行结束了, 所以这个错误是在Promise函数体外抛出的。

Node.js 有一个unhandledRejection事件,专门监听未捕获的reject错误。unhandledRejection事件的监听函数有两个参数, 第一个是错误对象, 第二个是报错的 Promise 实例, 它可以用来了解发生错误的环境信息。

process.on('unhandledRejection', function(err, p) {  console.error(err.stack)
});
复制代码

需要注意的是,catch方法返回的还是一个Promise对象,因此后面还可以接着调用then方法。

var someAsyncThing = function() {  return new Promise(function(resolve, reject) {  //  下面一行会报错,因为 x 没有声明  resolve(x + 2);  });
};
someAsyncThing()  .catch(function(error) {  console.log('oh no', error);  })  .then(function() {  console.log('carry on');  });
// oh no [ReferenceError: x is not defined]
// carry on
复制代码

上面代码运行完catch方法指定的回调函数,会接着运行后面那个then方法指定的回调函数。 如果没有报错, 则会跳过catch方法。

静态方法

像 Promise 这样的全局对象还拥有一些静态方法。

包括 Promise.all() 还有 Promise.resolve() 等在内,主要都是一些对Promise进行操作的 辅助方法。

Promise 状态

我们已经大概了解了Promise的处理流程,接下来让我们来稍微整理一下Promise的状态。

用 new Promise 实例化的promise对象有以下三个状态。

  • "has-resolution" 即Fulfilled resolve(成功)时。此时会调用 onFulfilled
  • "has-rejection" 即Rejected reject(失败)时。此时会调用 onRejected
  • "unresolved" 即Pending 既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态等

关于上面这三种状态的读法,其中左侧为在 ES6 Promises 规范中定义的术语, 而右侧则是在 Promises/A+ 中描述状态的术语。

promise对象的状态,从Pending转换为Fulfilled或Rejected之后, 这个promise对象的状态就不会再发生任何变化。也就是说,只有异步操作的结果可以决定当前是哪一种状态,其他任何操作都无法改变这种状态;一旦状态改变,就不会再改变。

Promise与Event等不同,在 .then 后执行的函数可以肯定地说只会被调用一次。

还有需要注意,Promise创建后回立刻执行,看下面代码

var promise = new Promise(function(resolve, reject) {  console.log('Promise');  resolve();
});
promise.then(function() {  console.log('Resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// Resolved
复制代码

上面代码中, Promise 新建后立即执行, 所以首先输出的是“ Promise”。 然后, then方法指定的回调函数, 将在当前脚本所有同步任务执行完才会执行, 所以“ Resolved” 最后输出。

Promise也是有缺点的

  • 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数, Promise内部抛出的错误,不会反应到外部。
  • 当处于Pending状态时, 无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

编写 Promise代码

下面介绍一下如何编写一下Promise代码。

创建Promise对象流程

  1. new Promise(fn)返回一个Promise对象
  2. fn中指定异步等处理逻辑 • 处理结果正常的话,调用 resolve(处理结果值) • 处理结果错误的话,调用 reject(Error对象)

按照这个流程,我们来写一段promise代码吧。任务是用Promise通过异步处理方式来获取XMLHttpRequest(XHR)的数据。

创建XHR的promise对象

首先,创建一个用Promise把XHR处理包装起来的名为 getURL 的函数。

function getURL(URL) {return new Promise(function (resolve, reject) {var req = new XMLHttpRequest(); req.open('GET', URL, true); req.onload = function () {if (req.status === 200) { resolve(req.responseText);} else {reject(new Error(req.statusText));} };req.onerror = function () { reject(new Error(req.statusText));};req.send(); });
}
// 运行示例
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){console.log(value);
}).catch(function onRejected(error){console.error(error);
});
复制代码

getURL 只有在通过XHR取得结果状态为200时才会调用 resolve,而其他情况(取得失败)时则会调用 reject 方法。

  • resolve(req.responseText) resolve函数的作用是, 将 Promise对象的状态从“ 未完成” 变为“ 成功”( 即从 Pending变为Resolved),在异步操作成功时调用,并将异步操作结果,作为参数传递出去。 参数并没有特别的规则,基本上把要传给回调函数参数放进去就可以了。 ( then 方法可以接收到这个参数值)

  • reject(new Error(req.statusText)); reject函数的作用是,将 Promise 对象的状态从“ 未完成” 变为“ 失败”( 即从 Pending 变为Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    上文中,XHR中 onerror事件被触发的时候就是发生错误时,所以理所当然调用 reject 。发生错误时,创建一个Error对象后再将具体的值传进去。传给 的参数也没有什么特殊的限制,一般只要是Error对象(或者 继承自Error对象)就可以。

Promise实例

Promise实现Ajax操作

var getJSON = function(url) {  var promise = new Promise(function(resolve, reject) {  var client = new XMLHttpRequest();  client.open("GET", url);  client.onreadystatechange = handler;  client.responseType = "json";  client.setRequestHeader("Accept", "application/json");  client.send();  function handler() {  if(this.readyState !== 4) {  return;  }  if(this.status === 200) {  resolve(this.response);  } else {  reject(new Error(this.statusText));  }  };  });  return promise;
};
getJSON("/posts.json").then(function(json) {  console.log('Contents: ' + json);
}, function(error) {  console.error(' 出错了 ', error);
});
复制代码

上面代码中,getJSON是对 XMLHttpRequest 对象的封装, 用于发出一个针对 JSON 数据的 HTTP 请求, 并且返回一个 Promise 对象。 需要注意的是,在getJSON内部, resolve函数和reject函数调用时, 都带有参数。关于参数传递,上文做过简要介绍

如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。 reject函数的参数通常是 Error 对象的实例,表示抛出的错误; resolve函数的参数除了正常的值以外,还可能是另一个 Promise实例, 表示异步操作的结果有可能是一个值,也有可能是另一个异步操作,比如像下面这样。

var p1 = new Promise(function(resolve, reject) {  // ...
});
var p2 = new Promise(function(resolve, reject) {  // ...  resolve(p1);
})
复制代码

上面代码中,p1p2都是 Promise 的实例, 但是p2resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作

注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是Pending,那么p2的回调函数就会等待p1的状态改变; 如果p1的状态已经是Resolved或者Rejected, 那么p2的回调函数将会立刻执行。

var p1 = new Promise(function(resolve, reject) {  setTimeout(() => reject(new Error('fail')), 3000)
})
var p2 = new Promise(function(resolve, reject) {  setTimeout(() => resolve(p1), 1000)
})
p2.then(result => console.log(result))
.catch(error => console.log(error))
复制代码

上面代码中,p1是一个 Promise, 3 秒之后变为rejectedp2的状态在 1 秒之后改变, resolve方法返回的是p1。 此时, 由于p2返回的是另一个Promise, 所以后面的then语句都变成针对后者( p1)。 又过了 2 秒 p1变为rejected,导致触发catch方法指定的回调函数。

使用bluebird爬虫实践

  • nodejs爬去慕课网文章
  • blue api

推荐文章

关于Promise 你真的了解多少? es6中的Promise JavaScript Promise 迷你书 中文版 JavaScript Promise 迷你书

Nodejs Promise 读书笔记相关推荐

  1. 《深入浅出NodeJS》读书笔记

     NodeJS NodeJS 4个特点:异步I/O,事件驱动与回调,单线程事件轮询,跨平台. NodeJS 5个大坑:异常处理,嵌套太深,没有Sleep,多线程编程,异步转同步. NodeJS 4 ...

  2. promise 读书笔记,promise核心方法(手写promise)

    promise 术语 1 promise 是一个有then方法的对象或者函数,行为遵循PromiseA+规范 2 thenable 是一个有then方法的函数或者对象 3 value promise ...

  3. 《深入浅出nodejs》读书笔记(3)

    挖坑,待填 转载于:https://www.cnblogs.com/yangzhou33/p/9972378.html

  4. Node.js: 深入浅出Nodejs读书笔记

    今天终于把朴灵老师写的<深入浅出Node.js>给学习 完了, 这本书不是一本简单的Node入门书籍,它没有停留在Node介绍或者框架.库的使用层面上,而是从不同的视角来揭示Node自己内 ...

  5. 现代前端技术解析读书笔记

    思维导图链接:http://v3.processon.com/view/link/5f7ec592762131119546c899 取材自<现代前端技术解析> 本文只是个人读书笔记,更多详 ...

  6. Build Your Own Angularjs 读书笔记(AngularJS牛逼的地方在于它内嵌了一个表达式到Function对象的编译器。。。当然还有DI框架)

    Build Your Own Angularjs 读书笔记 目录 [隐藏] 1 项目配置 2 作用域 3 表达式与过滤器 4 模块与依赖注入 5 辅助函数 6 指令 项目配置[编辑] npm pack ...

  7. JavaScript设计模式读书笔记(四)= 技巧型设计模式

    全系列目录 JavaScript设计模式读书笔记(一)=> 创建型设计模式 JavaScript设计模式读书笔记(二)=> 结构型设计模式 JavaScript设计模式读书笔记(三)=&g ...

  8. 白帽子讲WEB安全读书笔记(慢慢更新)

    道哥写的白帽子讲WEB安全的读书笔记 文章目录 2020.3.23 ◆ 前言 ◆ 第一篇 世界观安全 1.1 Web安全简史 >> 1.1.1 中国黑客简史 >> 1.1.2 ...

  9. 《Java8实战》读书笔记10:组合式异步编程 CompletableFuture

    <Java8实战>读书笔记10:组合式异步编程 CompletableFuture 第11章 CompletableFuture:组合式异步编程 11.1 Future 接口 (只是个引子 ...

最新文章

  1. QT学习之图形视图框架
  2. history linux 日志服务器_Linux日志分析
  3. Use After Free Tutorial
  4. Queue接口及是实现类PriorityQueue介绍
  5. IO:Reactor和Proactor的区别
  6. 骁龙660是32位还是64位_骁龙660是32位还是64位_都是搭载骁龙660处理器 这三款国产手机如何选...
  7. Typora markdown公式换行等号对齐_Typora编写博客格式化文档的最佳软件
  8. 文字在图片上c语言,HTML让文字在图片上显示的几种方法
  9. e-006 matlab,基于MATLAB进行潮流计算
  10. matlab对文件夹的遍历
  11. oracle数据库的监听配置
  12. Zabbix SNMP traps使用
  13. RS485通信和Modbus通信协议
  14. 两个强制屏幕旋转的方法
  15. poppin_xpower_ 常城
  16. horizon2206+A6000显卡 vGPU桌面经验分享
  17. 对象存储OSS适用于哪些场景
  18. 第四届“泰迪杯”数据分析技能赛-赛题A:《通讯产品销售和盈利能力分析》报告
  19. 26个手写大写字母图片
  20. 资管新规下机构众生相:缩表、转型及强者生存

热门文章

  1. 进制转换 二进制,八进制,十进制,十六进制互转
  2. 一元运算符 + 表示正号 - 表示负号
  3. js 正则知识汇总(转)
  4. linux系统update和upgrade区别
  5. MOOS程序解析记录(4)
  6. 安利一个自动求导网站
  7. poi 导出 word 表格样式
  8. 棋盘覆盖问题(分治)
  9. 在香港不能用GPRS上網(转)
  10. 30个Python最佳实践和技巧,你值得拥有~