es6之promise被坑记
2016-11-25 10:23
239 查看
promise的介绍就不多说了。
几个网址:
http://es6.ruanyifeng.com/#docs/promise
http://www.html5rocks.com/zh/tutorials/es6/promises/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
接下来看怎么被坑的。
先说说被坑的原因,主要是以前一直以为jQuery的deferred对象和promise是一样的api,所以一直没有深入研究,用jQuery也用习惯了,就一直放弃了promise的深入研究。
事实证明,浅尝辄止是可耻的,也是最容易被坑的。
代码1如下。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
当error为false的时候,输出结果
1
当error为true的时候,输出结果
2
1
2
然后我就傻眼了,搞什么啊,为什么明明失败了,成功的回调还会被调用呢?
然后开始疯狂google。
看到一个答案是:
当catch函数里没有抛出新的error时,promise认为错误已经被解决。
天真的我以为找到了问题的根源,于是简单的改成代码2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
然后重新运行,结果变成了
1
这个 Uncaught是个什么鬼?明明前面不是要抛出异常么?为什么又来个没有catch?
于是我开始怀疑我的写法的问题。
又开始改成代码3:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
结果变成了:
2
1
2
什么鬼?为什么改成这种写法以后又多出一个Uncaught。
此刻的我彻底惊呆了。开始怀疑自己的世界观了,认为自己到底是有多不小心,肯定哪里哪里的写法写错了,然后重复看上面的三篇基础介绍里的例子,对照自己的写法,始终没发现问题。
到了这一步的时候,我彻底凌乱了,一度觉得这个promise是什么鬼东西,还不如jquery的deferred呢。手放在删除键上犹豫了很久要不要改回jquery的deferred对象。
还好最后我忍住了冲动,重新认真的看了介绍。
总算明白了原因。
Promise是一种链式的回调,每一个then或者catch返回的是一个已经处理过的promise对象,同时catch只能catch之前链条中出现的错误。
那结合上面的那个说明,也就是说,我的catch函数因为是写在test函数内部的,所以它是这个链式回调的第一个环节,它只能catch promise的executor里的reject的分支,代码1中因为catch了reject分支,所以promise认为错误已经被处理了,自然会继续调用then的回调。
而代码2中,因为在catch中重新抛出了错误,而在这个catch之后的then里没有做错误的处理,于是出现了Uncaught的错误提示。
在代码3中,改了写法,return的是初始promise对象,所以实际上catch和then都属于第一链,在catch中因为抛出了新的error,故而出现一个Uncaught提示,而在then中没有定义reject的callback,且在then之后没有添加catch callback,所以会抛出第二个Uncaught提示。
问题总算找到了。
于是正确代码应该是
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
也就是将代码1中的catch移到then的后面即可。运行结果也就成为了预期结果
1
但是,问题是首先如果在之后再加入catch callback,根据前面的理论,下一个catch因为前面的reject已经被处理,所以第二个catch应该是不会运行的,那是不是意味着错误的回调只能写一次呢?
改成代码4
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
结果为:
1
果然,第二个catch没有运行。
那如果我在这个之后再加入一个then呢?会有什么样的结果?
代码5
2
1
2
至此已经基本明白promise的链式规则了。
1、每一个回调都接受上一个响应过的回调的返回值作为参数,代码5中,因为响应的是第一个catch callback,所以处理完错误后响应了第二个then的回调,而第二个then的参数则为第一个catch callback的返回值。如果error为false的话,第二个then的参数则变成了第一个then的返回值。
2、catch函数只能catch在链条之前发生的reject,同时,浏览器的错误也会被认为是reject状态,且reject的内容为错误提示。
这两点jQuery的deferred是和promise的实现方法是不同的。
jQuery的deferred的fail函数是有错误发生的时候就会响应,无论写在链式的什么位置。且,每一个callback的参数都为最初的resolve或者reject的值。
现在,回到最初我想解决的问题,如果我想要在test函数里统一处理reject,而test函数之外,只接受resolve的状态怎么实现?
要实现这个,其实也很简单,只需要保证catch不是在then之后就可以了,也就是将代码2里的throw部分改成处理错误既可。于是改成代码6
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
当我得意洋洋以为理解透彻了的时候,结果又让我吓了一跳
2
1
2
什么鸟,为什么还有Uncaught的提示?
冷静想想,首先,Uncaught的意思是代表有reject状态没有被catch,那没有被catch的地方只有可能是在test函数外的then后面。按照前面的理论,代码6的写法里,catch完以后我返回了原本的promise对象,而原本的promise对象里reject的状态其实是没有catch的。所以才会出现Uncaught的提示。
这样一来,好吧,只能丢弃jQuery的deferred的影响。
如果要在test里处理错误,就只能在异步的函数里进行处理,也就是将setTimeout的reject去掉,只留下resolve,然后每一个then都处理resolve,并且返回直接将参数作为返回值。
但是这样一来,在test外就没有办法再次调用错误回调了。
好吧,看来是我的要求比较绕,要求本身是和jQuery的deferred的功能是一致的。
几个网址:
http://es6.ruanyifeng.com/#docs/promise
http://www.html5rocks.com/zh/tutorials/es6/promises/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
接下来看怎么被坑的。
先说说被坑的原因,主要是以前一直以为jQuery的deferred对象和promise是一样的api,所以一直没有深入研究,用jQuery也用习惯了,就一直放弃了promise的深入研究。
事实证明,浅尝辄止是可耻的,也是最容易被坑的。
代码1如下。
var error = true; function test(){ //promise处理异步函数的回调,这里简单的用一个error代表正确结果和非预期结果的判断条件 var promise = new Promise(function(resolve,reject){ setTimeout(function(){ if(!error){ resolve("没错"); }else{ reject("有错"); } },100) }); //test函数内部处理非预期结果。 return promise.catch(function(error){ console.log("失败了:"+error); }); } test().then(function(str){ console.log("成功了:"+str); });1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
当error为false的时候,输出结果
成功了:没错1
1
当error为true的时候,输出结果
失败了:有错 成功了:undefined1
2
1
2
然后我就傻眼了,搞什么啊,为什么明明失败了,成功的回调还会被调用呢?
然后开始疯狂google。
看到一个答案是:
当catch函数里没有抛出新的error时,promise认为错误已经被解决。
天真的我以为找到了问题的根源,于是简单的改成代码2
var error = true; function test(){ var promise = new Promise(function(resolve,reject){ setTimeout(function(){ if(!error){ resolve("没错"); }else{ reject("有错"); } },100) }); return promise.catch(function(error){ //没错,就是改了这里而已 throw new Error("失败了:"+error) }); } test().then(function(str){ console.log("成功了:"+str); });1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
然后重新运行,结果变成了
Uncaught (in promise) Error: 失败了:有错1
1
这个 Uncaught是个什么鬼?明明前面不是要抛出异常么?为什么又来个没有catch?
于是我开始怀疑我的写法的问题。
又开始改成代码3:
var error = true; function test(){ var promise = new Promise(function(resolve,reject){ setTimeout(function(){ if(!error){ resolve("没错"); }else{ reject("有错"); } },100) }); promise.catch(function(error){ throw new Error("失败了:"+error) }); return promise; } test().then(function(str){ console.log("成功了:"+str); });1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
结果变成了:
Uncaught (in promise) Error: 失败了:有错 Uncaught (in promise) 有错1
2
1
2
什么鬼?为什么改成这种写法以后又多出一个Uncaught。
此刻的我彻底惊呆了。开始怀疑自己的世界观了,认为自己到底是有多不小心,肯定哪里哪里的写法写错了,然后重复看上面的三篇基础介绍里的例子,对照自己的写法,始终没发现问题。
到了这一步的时候,我彻底凌乱了,一度觉得这个promise是什么鬼东西,还不如jquery的deferred呢。手放在删除键上犹豫了很久要不要改回jquery的deferred对象。
还好最后我忍住了冲动,重新认真的看了介绍。
总算明白了原因。
Promise是一种链式的回调,每一个then或者catch返回的是一个已经处理过的promise对象,同时catch只能catch之前链条中出现的错误。
那结合上面的那个说明,也就是说,我的catch函数因为是写在test函数内部的,所以它是这个链式回调的第一个环节,它只能catch promise的executor里的reject的分支,代码1中因为catch了reject分支,所以promise认为错误已经被处理了,自然会继续调用then的回调。
而代码2中,因为在catch中重新抛出了错误,而在这个catch之后的then里没有做错误的处理,于是出现了Uncaught的错误提示。
在代码3中,改了写法,return的是初始promise对象,所以实际上catch和then都属于第一链,在catch中因为抛出了新的error,故而出现一个Uncaught提示,而在then中没有定义reject的callback,且在then之后没有添加catch callback,所以会抛出第二个Uncaught提示。
问题总算找到了。
于是正确代码应该是
var error = true; function test(){ var promise = new Promise(function(resolve,reject){ setTimeout(function(){ if(!error){ resolve("没错"); }else{ reject("有错"); } },100) }); return promise } test().then(function(str){ console.log("成功了:"+str); }).catch(function(error){ console.log("失败了:"+error) });1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
也就是将代码1中的catch移到then的后面即可。运行结果也就成为了预期结果
失败了:有错1
1
但是,问题是首先如果在之后再加入catch callback,根据前面的理论,下一个catch因为前面的reject已经被处理,所以第二个catch应该是不会运行的,那是不是意味着错误的回调只能写一次呢?
改成代码4
var error = true; function test(){ var promise = new Promise(function(resolve,reject){ setTimeout(function(){ if(!error){ resolve("没错"); }else{ reject("有错"); } },100) }); return promise } test().then(function(str){ console.log("成功了:"+str); }).catch(function(error){ console.log("失败了:"+error) }).catch(function(error){ console.log("第二个回调:"+error); });1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
结果为:
失败了:有错1
1
果然,第二个catch没有运行。
那如果我在这个之后再加入一个then呢?会有什么样的结果?
代码5
失败了:有错 第二个成功回调:有错1
2
1
2
至此已经基本明白promise的链式规则了。
1、每一个回调都接受上一个响应过的回调的返回值作为参数,代码5中,因为响应的是第一个catch callback,所以处理完错误后响应了第二个then的回调,而第二个then的参数则为第一个catch callback的返回值。如果error为false的话,第二个then的参数则变成了第一个then的返回值。
2、catch函数只能catch在链条之前发生的reject,同时,浏览器的错误也会被认为是reject状态,且reject的内容为错误提示。
这两点jQuery的deferred是和promise的实现方法是不同的。
jQuery的deferred的fail函数是有错误发生的时候就会响应,无论写在链式的什么位置。且,每一个callback的参数都为最初的resolve或者reject的值。
现在,回到最初我想解决的问题,如果我想要在test函数里统一处理reject,而test函数之外,只接受resolve的状态怎么实现?
要实现这个,其实也很简单,只需要保证catch不是在then之后就可以了,也就是将代码2里的throw部分改成处理错误既可。于是改成代码6
var error = true; function test(){ var promise = new Promise(function(resolve,reject){ setTimeout(function(){ if(!error){ resolve("没错"); }else{ reject("有错"); } },100) }); promise.catch(function(error){ console.log("失败了:"+error) return error; }) return promise; } test().then(function(str){ console.log("成功了:"+str); })1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
当我得意洋洋以为理解透彻了的时候,结果又让我吓了一跳
失败了:有错 Uncaught (in promise) 有错1
2
1
2
什么鸟,为什么还有Uncaught的提示?
冷静想想,首先,Uncaught的意思是代表有reject状态没有被catch,那没有被catch的地方只有可能是在test函数外的then后面。按照前面的理论,代码6的写法里,catch完以后我返回了原本的promise对象,而原本的promise对象里reject的状态其实是没有catch的。所以才会出现Uncaught的提示。
这样一来,好吧,只能丢弃jQuery的deferred的影响。
如果要在test里处理错误,就只能在异步的函数里进行处理,也就是将setTimeout的reject去掉,只留下resolve,然后每一个then都处理resolve,并且返回直接将参数作为返回值。
但是这样一来,在test外就没有办法再次调用错误回调了。
好吧,看来是我的要求比较绕,要求本身是和jQuery的deferred的功能是一致的。
相关文章推荐
- ES6 Promise
- ES6之Promise
- es6 Promise.prototype.then()方法
- ES6 学习之promise 基本用法
- ES6之Promise
- es6之Promise对象笔记
- es6 promise对象回调处理详解
- 自学-ES6篇-Promise对象
- ES6之Promise学习之路
- es6 Promise 的应用
- ES6 -- Promise对象
- Angular2 之 Promise vs Observable
- AngularJS 中的Promise --- $q服务详解
- FunDA(6)- Reactive Streams:Play with Iteratees、Enumerator and Enumeratees
- ES权威指南_04_aggs_05 Scoping Aggs
- JS(六)promise
- ES6之Babel的各种坑总结
- 深入 Promise(一)——Promise 实现详解
- Es学习总结说明书
- Elasticsearch之重要核心概念(cluster(集群)、shards(分配)、replicas(索引副本)、recovery(据恢复或叫数据重新分布)、gateway(es索引的持久化存储方式)、discovery.zen(es的自动发现节点机制机制)、Transport(内部节点或集群与客户端的交互方式)、settings(修改索引库默认配置)和mappings)