"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) 原型上的方法的更多相关文章

  1. JavaScript数组方法总结,本文是根据数组原型上的方法进行总结,由于方法太多将会分篇章发布

    通过浏览器控制台 console 可查看到 Array 数组上原型的所有方法(如下图).对于原型问题此文章暂不过多叙述,单针对对象中的方法进行自我看法的总结:细心的同学可以发现对象原型上所携带的方法基 ...

  2. call()和原型继承的方法

    1.call() call()方法接受两个参数,obj和arg 比如functionA.call(obj,arg)   就是说现在运行(执行)functionA这个方法,但是functionA里面的方 ...

  3. 基于原生JS封装数组原型上的sort方法

    基于原生JS封装数组原型上的sort方法 最近学习了数组的原型上内置方法的封装,加强了用原生JS封装方法的能力,也进一步理解数组方法封装的过程,实现的功能.虽然没有深入底层,了解源码.以下解法都是基于 ...

  4. 3分钟教会你把封装的js公共方法挂载在vue实例原型上

    第一步:首先在src文件夹里面创建一个通用js文件夹,然后在创建的文件夹里面创建一个js文件 第二步:const 一个方法,然后通过export暴露出来(在同一个页面可以写多个方法,和暴露多个方法,在 ...

  5. JavaScript利用数组原型,添加方法实现遍历多维数组每一个元素

    原型就是提供给我们为了让我们扩展更多功能的. 今天学习了用js模拟底层代码,实现数组多维的遍历.思想是在数组原型上添加一个方法. // js中的数组forEach方法,传入回掉函数 能够帮助我们遍历数 ...

  6. jq源码解析之绑在$,jQuery上面的方法

    1.当我们用$符号直接调用的方法.在jQuery内部是如何封装的呢?有没有好奇心? // jQuery.extend 的方法 是绑定在 $ 上面的. jQuery.extend( { //expand ...

  7. 六.jQuery源码分析之jQuery原型属性和方法

    97 jQuery.fn = jQuery.prototype = { 98 constructor: jQuery, 99 init: function( selector, context, ro ...

  8. react-native-pg-utils(对react-native全局进行配置,对内置对象原型链增加方法,增加常用全局方法.)

    react-native-pg-utils 对react-native全局进行配置,对内置对象原型链增加方法,增加常用全局方法. 每次新建react-native项目之后都会发现有一些很常用的方法在这 ...

  9. ES6 class 语法糖不能直接定义原型上的属性

    今天注意到两个东西: 1.为了模拟面向对象,JavaScript的class语法糖屏蔽了原型的概念 class A{ a = 1   // 注意!!这里定义的不是在prototype上的属性,而是给实 ...

随机推荐

  1. Sqlserver 锁(转)

    转载 http://www.cnblogs.com/zhouqianhua/archive/2011/04/15/2017049.html 总结:    锁的种类: 共享锁\排它锁\更新锁\意向锁\计 ...

  2. sklearn模型的属性与功能-【老鱼学sklearn】

    本节主要讲述模型中的各种属性及其含义. 例如上个博文中,我们有用线性回归模型来拟合房价. # 创建线性回归模型 model = LinearRegression() # 训练模型 model.fit( ...

  3. redis启动过程源码解析

    redis整个程序的入口函数在server.c中的main函数,函数调用关系如下图1,调用顺序为从上到下,从左至右. 图1 redis启动函数调用图 main函数源码如下,1-55行根据配置文件和启动 ...

  4. [wordpress]WordPress地址(URL)错误,修改解决方案

    本人在修改Wordpress地址(URL)时,误操作使URL指向错误,后台无法进入. 解决方案 1.先利用Putty登陆到自己的服务器上(这里登陆方法我不再赘述): 2.登陆MySqL,并输入密码: ...

  5. HttpSession与JSESSIONID的"盗用"

    https://blog.csdn.net/qq1437715969/article/details/75331652

  6. Ubuntu文件写入内容时出现错误 E121:无法打开并写入文件解决方案

    在安装某些软件过程中会让你新建个txt或者在输入vim /etc/profile命令时,输入完毕后保存文件时就会报错 E121:无法打开并写入文件解决方案,一般的解决状况就是输入如下命令: :w !s ...

  7. 自己总结的C#编码规范--7.文档下载 & 总结

    今天终于把这一系列的编码规范写完了,这个编码规范算上前面阅读相关书籍,前前后后总共花了一个月的时间,也算是个人的呕心沥血之作了. 本来也没打算把这个系列写的这么长,但是在写的过程中自己搜了相关的网上资 ...

  8. Docker操作笔记(一)使用镜像

    使用镜像 一)获取镜像 从Docker镜像仓库获取命令的格式是: docker pull [选项] [Docker Registry 地址[:端口号]] 仓库名[:标签] 具体的选项可以通过docke ...

  9. # Do—Now——团队冲刺博客_总结篇

    Do-Now--团队冲刺博客_总结篇 目录 博客链接 作者 1. 第一篇(领航篇) @仇夏 2. 第二篇 @侯泽洋 3. 第三篇 @仇夏 4. 第四篇 @周亚杰 5. 第五篇 @唐才铭 6. 第六篇 ...

  10. Java 基础 Java平台的3个版本

    Java平台有3个版本:适用于小型设备和智能卡的JavaME(Java Platform Micro Edition,Java微型版).适用于桌面系统的JavaSE(Java Platform Sta ...