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上的属性,而是给实 ...
随机推荐
- git操作笔记《二》:github更新缓慢问题的解决办法
从GitHub上拉取代码速度十分之慢,百度了一下,说是github的某些域名的dns解析被污染了. 解决方法: 方案一:可以花钱购买VPN服务,但是这对于学生党来说是不划算的. vpn 方案二:绕过d ...
- sort it 树状数组+逆序对
sum[i]是1-i所有1的和,而i-sum[a[i]]就是在a[i]后面的数,即在i之前出现,却比他大的数.1是加在a[i]上,即i实际应该放的位置.而c[i]是为sum做准备的 #include& ...
- rem实现移动端自适应页面
一.把px转换成rem方案 1.cssrem插件 2.css预处理器 3.rem-unit插件 4.px2rem插件 rem就是相对于根元素的font-size来做计算,设置好根结点字体大小,子节点用 ...
- Jquery中事件的重复绑定
问题:事件的绑定具有叠加效果,已有绑定的函数时,再次绑定,执行时会出现绑定多少次,触发多少次的情况 解决:1.on();绑定事件,提供了绑定事件处理程序所需的所有功能,用于统一取代以前的bind(). ...
- cmd应用基础 扫盲教程
cmd是什么? 对于程序员而言,cmd命令提示符是windows操作系统下一个比较重要的工具.对于程序员而言,为了追求更高的效率而抛弃花俏的界面已然是意见很常见的行为,截止到目前的,全世界仍有大量的服 ...
- HTML入门9
这一篇着眼于HTML里的音频和视频标签及相关处理: 传统技术不能再web中使用音频和视频,一致使用Flash后来因为一些HTML/CSS特性,安全问题,慢慢退出.在HTML5提出后,新特性<vi ...
- 18 徐州 M
听了遍dls的讲解觉得这是个沙比题,结果调了两个小时... 主要注意的点有两个, 一个是 找每个灯覆盖的区间,这个用叉积看一下夹角即可 一个是 覆盖的时候点覆盖比边覆盖好写(个人感觉) 点覆盖的话,如 ...
- JMeter参数化中存在逗号的解决方法
在Jmeter中通过CSV Data Set Config进行参数化时,如果参数化数据中存在逗号(,)我们可以通过一下方式进行设置 如何存在中文乱码,可以设置file encoding:gb2312
- WIN10远程计算机不支持所需的FIPS安全级别解决
win10系统的电脑在远程xp系统或者其他系统的电脑时,提示错误,远程计算机可能不支持所需的FIPS安全级别,如果出现一以下2种错误,可以解决! 1 第一步:打开win10下的,控制面板 2 第二 ...
- workerman 安装event 扩展
2018年7月31日10:07:47 一些小技巧 命令行直接运行PHP代码 php -r "phpinfo();" 交互模式运行PHP php -a PHP脚本作为shell脚本运 ...