Promise (2) 原型上的方法
"I'm Captain Jack Sparrow"
加勒比海盗5上映,为了表示对杰克船长的喜爱,昨天闪现了几次模仿船长的走路姿势(哈哈哈,简直妖娆)。
为了周天能去看电影,要赶紧做完手上的活儿,比如总结Promise的方法。
2 Promise基本方法简介
Promise提供了哪些方法了?大招就是放图在控制台输出Promise。
从图中结构看,Promise构造函数上实现了all,race,reject,resolve。Promise构造函数的原型上实现了then,catch的方法。在构造函数原型上实现then,catch的方法是为了让Promise构造函数创建的 实例 共享then,catch方法。(此处提一下,实例和构造函数原型之间存在连接,并不是与构造函数存在连接。对构造函数原型和构造函数,实例之间的关系不理解可以看看《javascript高级程序设计》第六章)。 在Promise构造函数上实现的all,race,reject,resolve,不能在对象的实例中访问,属于Promise构造函数自己,这样做保证了对象的命名空间整洁。所以这几个函数的调用方式是Promise.all(),Promise.race(),Promise.reject(),Promise.resolve()。
Promise简体实现结构大概是:
//Promise构造函数实现的大概结构
function Promise(resolver) {
//promise 的初始状态
this._PromiseStatus = 'pending';
//promise 的value 值(不同实现这个属性名字不一样)
this._PromiseValue = undefined
//......接下来要实现的此处省略
} //给Promise添加方法
Promise.all = all;
Promise.race = race;
Promise.resolve = resolve;
Promise.reject = reject; //重写Promise构造函数原型
Promise.prototype = {
constructor: Promise,
then: then,
catch: catch
}; //方法具体实现
function all( /***/ ) { }
// ......方法实现省略
使用new 操作符创建 promise对象实际经历的步骤:(这个摘抄自《javascript高级程序设计》,好书值得多读几遍)
1.创建了一个新的对象。
2.将构造函数的作用域赋给新对象(因此this就指向了这个新的对象)。
3.执行构造函数代码(为这个新对象添加属性)。
4.返回新对象。
第三条加粗了可以解释为什么all,race,reject,resolve,不能在对象的实例中访问。all,race,reject,resolve并没有在构造函数中赋值给新对象的属性。
总结:then,catch方法是供Promise构造函数创建的 实例调用的,all,race,reject,resolve是Promise构造函数自己调用的。(这句描述不是很标准)
2.1 Promise.prototype.then
⑴.为什么promise对象需要状态?
var testPromiseStatus = new Promise(function(resolve, reject) {
//异步操作......成功后执行了resolve(1)
getdata((data) => {
resolve(data);
})
}) setTimeout(() => {
//从创建了testPromiseStatus,在到执行testPromiseStatus.then中间的时间间隔不确定
testPromiseStatus.then(function onFulfilled(value) {
console.log("我到底应该什么时候执行呀?");
})
},一个不确定的时间)
需要一个状态位去标志,testPromiseStatus 的状态,
如果在执行testPromiseStatus.then()时promise的状态是pedding,将 onFulfilled存起来 等到promise状态变成fulfilled的时候执行。
如果 testPromiseStatus.then()时promise的状态是fulfilled,直接执行onFulfilled函数。
参考es6-promise的源码,会发现它的实现是将回调函数放进一个数组队列(意味着队列里面不一定只有一个函数等待被执行),然后如果promise对象状态不为pending状态,就按顺序执行这个 数组队列里面的回调函数,如果promise对象为pending状态,就是等待promise对象状态迁移后再执行这个数组队列。
- 如果promise对象状态不为pending状态
- 如果promise对象为pending状态,就是等待promise对象状态迁移后再执行这个数组队列
⑵.为什么Promise.prototype.then执行它会返回一个新的promise对象?
var aPromise = new promise(function(resolve, reject) {
resolve("test chain");
});
aPromise.then(function taskA(value) {
// task A
}).then(function taskB(vaue) {
// task B
})
返回新的promise对象的原因是为了链式操作,使得能以taskA → task B 这种流程进行逻辑处理。即aPromise.then()执行完,返回一个新promise b对象,接着执行新promise b对象的then方法,为什么是新的promise对象?因为promise对象需要自己的状态[[PromiseStatus]]值(记录自己的状态)(用来判定是执行then传递的函数,还是等待promise状态迁移后,再执行then里面的函数),每一个还需要自己的 [[PromiseValue]] 值(记录自己的值,当promise状态迁移后,.then的函数每一次调用得到的参数data一致)。所以需要返回一个新的promise对象。
⑶.thenable对象是个啥?
类Promise对象,thenable对象拥有名为then方法的对象。所拥有的 then 方法应该和Promise所拥有的 then 方法具有同样的功能和处理过程。then 调用的回调函数 retuen 一个普通的thenable对象,会先执行thenable对象的then方法,根据then方法内部执行resolve或reject确定[[PromiseValue]]和[[PromiseStatus]],然后将值赋给新的promise对象。可以在控制台执行下图代码。
var thenable = {
then: function(resolve, reject) {
console.log("thenable");
reject("thenable");
}
};
var testPromiseThenable = new Promise(function(resolve, reject) {
resolve('haha');
}).then(function onFulfilled() {
//返回一个thenable对象
return (thenable);
})
总结:Promise的状态是为了解决then方法传递的回调是等待被执行(函数队列),还是立即执行(函数队列)。Promise的链式操作的需求和每一个promise对象都需要自己的状态,让then方法返回的总是一个新的promise对象。
then方法执行的回调函数执行return 值(参考至Promises/A+。这篇是翻译,个人觉得很棒【翻译】Promises/A+规范,建议文章很简短值得一看)
①var aPromise = new Promise(function(){ resolve(1)}); var testPromise = aPromise.then(null,null)//没有回调函数,新创建的testPromise 对象的[[PromiseStatus]]和[[PromiseValue]]与之前的aPromise 的一样。
②then方法执行的回调函数执行return 值只为普通对象或者原始类型,即将值赋给新创建的promise对象[[PromiseValue]],新创建的promise对象的[[PromiseStatus]]就等于调用then的那个promise对象的[[PromiseStatus]]。
③then方法执行的回调函数执行return值为promise对象,即将promise对象的[[PromiseValue]]和[[PromiseStatus]]赋值给新创建的promise对象对应属性值。
④then方法执行的回调函数执行return值为thenable对象,即执行对象的then方法,根据then方法内部执行resolve或reject确定[[PromiseValue]]和[[PromiseStatus]],赋值给新创建的promise对象对应属性值。
2.2 Promise.prototype.catch
Promise.prototype.catch 只是 promise.prototype.then(undefined, onRejected)方法的一个别名而已。 也就是说,这个方法用来注册当promise对象状态变为Rejected时的回调函数。摘抄至--《javascript Promise迷离书》
promise.prototype.then(onFulfilled, onRejected)中的onRejected是不会处理同级的onFulfilled的函数的错误的,他处理的是前一个promise对象的。传递给Promise.prototype.catch 的参数是一个函数,这个函数主要的作用是处理之前的错误。
⑴.调用catch 干什么?
var testPromiseMistake = new Promise(function(resolve, reject) {
reject("testPromiseMistake")
});
//使用catch 处理错误
var rejectPromiseCatch = testPromiseMistake.catch(function onRejected(value) {
console.log("rejectPromiseCatch:", value)
})
//使用then处理错误
var rejectPromiseThen = testPromiseMistake.then(null, function onRejected (value) {
console.log("rejectPromiseThen:", value)
})
上面用catch和then是等价的。catch就是处理之前的promise抛出的异常。
有的文章中说testMistake.catch是不会执行的,因为前面没有错误抛出,会跳过.catch方法。但testMistake.catch它是执行了的,下图代码中 testMistake 和 testMistakecatch 是不相等的promise对象,可以在控制台输出判断。Promise.prototype.catch 与 promise.prototype.then(undefined, onRejected)方法等价,没有错误时就像执行了then但它的回调函数onFulfilled为null而已。
var testMistake = new Promise(function(resolve, reject) {
resolve("1")
})
var testMistakecatch = testMistake.catch(function(value) {
console.log("Catch:", value)
})
⑵.catch调用后也会产生新的promise
如图catch之后还是可以调用catch或者then方法的。在then函数中怎么显示抛出异常或者在catch中怎么显示抛出异常,让错误可以一直传递下去?(妈蛋,有错误就处理了,还传递个毛线呀)这就是后文中提起的Promise.reject();
对了catch的err来源于上一个Preomise的[[PromiseValue]]值看下图代码。
var haha = {
'xiaobu': 2017,
'data': '6/13'
} var testC = new Promise(function(resolve, reject) {
reject(haha);
})
// 此处判定
testC.catch(e => {
console.log(e === haha)
})
此处可以在控制台输出true 和 testC,发现testC[[PromiseValue]]值与haha相等。
总结:清楚Promise.prototype.catch 只是 promise.prototype.then(undefined, onRejected)方法的一个别名,懂then的调用那么catch也不是什么新方法了。
2.3 总结
"I'm Captain Jack Sparrow"
看完加勒比海盗好几天,这篇学习笔记还是没写完。愧疚一把,为了结束,就总结为原型上的方法篇吧。
Promise.prototype.catch和promise.prototype.then 是需要认真理解的方法。理解Promise的状态,理解then方法调用会返回新的promise对象等等。
ps. node从7.6版本开始就支持async/await。async/await是一种编写异步的新方法,可以让代码看起来,表现起来更像同步代码……优点一堆值得体验。(当然理解Promise,对理解async/await 是有帮助的)
Promise (2) 原型上的方法的更多相关文章
- JavaScript数组方法总结,本文是根据数组原型上的方法进行总结,由于方法太多将会分篇章发布
通过浏览器控制台 console 可查看到 Array 数组上原型的所有方法(如下图).对于原型问题此文章暂不过多叙述,单针对对象中的方法进行自我看法的总结:细心的同学可以发现对象原型上所携带的方法基 ...
- call()和原型继承的方法
1.call() call()方法接受两个参数,obj和arg 比如functionA.call(obj,arg) 就是说现在运行(执行)functionA这个方法,但是functionA里面的方 ...
- 基于原生JS封装数组原型上的sort方法
基于原生JS封装数组原型上的sort方法 最近学习了数组的原型上内置方法的封装,加强了用原生JS封装方法的能力,也进一步理解数组方法封装的过程,实现的功能.虽然没有深入底层,了解源码.以下解法都是基于 ...
- 3分钟教会你把封装的js公共方法挂载在vue实例原型上
第一步:首先在src文件夹里面创建一个通用js文件夹,然后在创建的文件夹里面创建一个js文件 第二步:const 一个方法,然后通过export暴露出来(在同一个页面可以写多个方法,和暴露多个方法,在 ...
- JavaScript利用数组原型,添加方法实现遍历多维数组每一个元素
原型就是提供给我们为了让我们扩展更多功能的. 今天学习了用js模拟底层代码,实现数组多维的遍历.思想是在数组原型上添加一个方法. // js中的数组forEach方法,传入回掉函数 能够帮助我们遍历数 ...
- jq源码解析之绑在$,jQuery上面的方法
1.当我们用$符号直接调用的方法.在jQuery内部是如何封装的呢?有没有好奇心? // jQuery.extend 的方法 是绑定在 $ 上面的. jQuery.extend( { //expand ...
- 六.jQuery源码分析之jQuery原型属性和方法
97 jQuery.fn = jQuery.prototype = { 98 constructor: jQuery, 99 init: function( selector, context, ro ...
- react-native-pg-utils(对react-native全局进行配置,对内置对象原型链增加方法,增加常用全局方法.)
react-native-pg-utils 对react-native全局进行配置,对内置对象原型链增加方法,增加常用全局方法. 每次新建react-native项目之后都会发现有一些很常用的方法在这 ...
- ES6 class 语法糖不能直接定义原型上的属性
今天注意到两个东西: 1.为了模拟面向对象,JavaScript的class语法糖屏蔽了原型的概念 class A{ a = 1 // 注意!!这里定义的不是在prototype上的属性,而是给实 ...
随机推荐
- 课堂小记---html
其他注意点: 行高line-hight的继承特性: 行高有三种属性值:数字(1.5).百分比(150%).长度值(1.5em或者30px).继承上这三者是有区别的. 当属性值为数字值,其子元素会继承行 ...
- 前后台得到WEB应用的名称
前台得到当前应用的名称: ${ pageContext.request.contextPath } 后台得到当前应用的名称: request.getServletContext().getContex ...
- Python学习之MySQLdb模块
摘要: MySQLdb模块用于操作mysql数据库.1.安装MySQLdb模块 yum install MySQL-python -y2.操作流程①.导入模块: import MySQLdb②.连接数 ...
- 动态规划——Split Array Largest Sum
题意大概就是,给定一个包含非负整数的序列nums以及一个整数m,要求把序列nums分成m份,并且要让这m个子序列各自的和的最大值最小(minimize the largest sum among th ...
- 06-JDBC
JDBC > JAVA Database Connectivity java 数据库连接 * 为什么会出现JDBC > SUN公司提供的一种数据库访问规则.规范, 由于数据库种类较多,并且 ...
- Mac_IntelliJ IDEA For Mac 快捷键
Mac键盘符号和修饰键说明 ⌘ Command ⇧ Shift ⌥ Option ⌃ Control ↩︎ Return/Enter ⌫ Delete ⌦ 向前删除键(Fn+Delete) ↑ 上箭头 ...
- WinForm打包或部署
一.新建InstallShield项目 二. 三. 四. 五. 六. 七. 最后重新生成,安装包一般在E\Setup1\Setup1\Express\SingleImage\DiskImages\DI ...
- openlayers应用原理
1.数据组织 OpenLayers通过同层(Layer)进行组织渲染,然后通过数据源设置具体的地图数据来源.因此,Layer与Source是密切相关的对应关系,缺一不可.Layer可看做渲染地图的层容 ...
- DevExpress控件GridControl中的布局详解 【转】
DevExpress控件GridControl中的布局详解 [转] 2012-10-24 13:27:28| 分类: devexpress | 标签:devexpress |举报|字号 订阅 ...
- DevPress GridControl的使用
XtraGrid使用方法 XtraGrid的关键类就是:GridControl和GridView.GridControl本身不显示数据,数据都是显示在GridView/CardView/XXXXV ...