网上没找到很有收获的文章, 所以自己总结了一下:
reject()
throw
两种方法效果基本是等同的, 区别在于 reject() 不会终止函数的执行.
只有最先抛出的错误会被捕获.
比如先 reject()
一个错误, 再 throw
第二个错误, 捕获到的是第一个错误, 但第二个抛出的错误也会被执行, 不过只有终止函数的作用了, 相当于 return
.
网上有人建议不要使用 throw
抛出错误, 因为 throw 并不会更改 promise 的状态, 而且容易和系统抛出的其他异常混淆. 我认为这个建议值得继续探讨而不是下定论, 首先, promise 的状态并不能直接监控, 其次抛出什么错误我们完全可以扩展 Error 而区分错误类型.
我的建议是没有对错, 分具体情况使用就好.
reject()
的优点, 不影响函数继续执行, 没有 throw
的作用域限制.
throw
的优点, 不需要在 Promise 中, 在 then()
中的普通函数中也能抛出错误.
Promise 内部抛出的错误是无法被外部的 try...catch 捕获的, 因为作用域问题.
then()
的第二个参数catch()
的第一个参数错误的处理都是调用一个函数 (onRejected), 传入一个参数, 这个参数即为刚才抛出的错误.
错误只会被捕获一次. 且如果错误被捕获, 则之后的 then()
都会被执行, 即使不拥有正常的返回值作为参数. 因此建议链式调用最末端再捕获错误.
这两种方法没有什么区别, 无论是如何抛出错误, 都能被正常捕获.
1
mopig 2017-03-10 18:32:23 +08:00
> 因为 throw 并不会更改 promise 的状态
这句话是啥意思,我测试好像是会改状态的。 |
2
mcfog 2017-03-10 18:36:00 +08:00
感觉楼主基本没理解到点子上
|
3
AlphaTr 2017-03-10 18:37:28 +08:00 via iPhone
throw 和 reject 理解错了, promise 会隐式对 throw 的错误 try catch 后 reject 掉
|
4
m31271n 2017-03-10 18:44:07 +08:00
|
6
iugo OP @AlphaTr 我只是用, 还没看过 polyfill 具体实现的代码. 我觉得我说的和 "promise 会隐式对 throw 的错误 try catch 后 reject 掉" 没有冲突.
代码例子: ``` // 适合使用 reject() new Promise((resolve, reject) => { someFunc({ cb() { reject() } }) }).catch(eh) // 适合使用 throw new Promise(resolve => { resolve(data) }).then(data => { const res = handle(data) if (!res) { throw new Error('error') } }).catch(eh) ``` |
9
AlphaTr 2017-03-10 20:11:58 +08:00 via iPhone 1
@iugo 先说 throw 的优点,其实并不是在 promise 中的特性,而是 throw 本身的特性,而在一般使用中 return reject() 伴随着使用,常用场景就是遇到错误不继续执行,而 下面说的 在 then 中,同样可以使用 return Promise.reject() 达到 throw 同样的效果,所以结论就是这两种使用起来基本没有区别,可以互相替换着
|
10
AlphaTr 2017-03-10 20:13:35 +08:00 via iPhone
@iugo 少补充一点, throw 只能在同步场景中使用, reject 可以用于异步,可能就是两个的区别吧
|
11
mcfog 2017-03-10 21:56:19 +08:00 via Android 2
@iugo 首先可以去看看 promise/A+的全文,创建 promise 的方法也好,.catch 也好都不在标准范围内。 throw 和 reject 行为的差异也好, catch 后的 then 为什么继续运行也好,都在标准里有,虽然有点拗口 /晦涩,但却是直指核心的
如果你理解了的话就不会用”捕获错误“这样的描述来讲这个问题了 另外作为练习,建议你想想怎么用 promise 来完成一次递归。就比如说远程的分页拉信息的 api 如果不会返回总条数 /页数,只通过返回空列表标志列表结束,如何用返回 promise 的方法 getList(pageNo)来说构造一个拉完整列表的 getWholeList(),在第 n 页出错的时候用{currentPage:n, error:e}结构来 reject 你返回的 promise 对象 |
12
iugo OP @mcfog 我写的都是使用经验, 没有看标准的定义.
ECMA 262 6th Promise Abstract Operations, Promise Jobs 让我感觉似懂非懂. 因为标准中有许多关联, 要把关联搞懂才能更好地继续阅读, 比如 Record, Reaction, Job 等等. 关于错误这块儿, throw 好像是 PromiseReaction[[Handler]] is "Thrower", handlerResult be Completion, 正常应该是 handlerResult be Call. 但我不确定我看懂了... 就我目前来说, 还是使用吧, 依靠经验... |
13
iugo OP @mcfog 做了一下练习题, 感觉实现了功能, 但没发现自己存在的问题...
``` const api = (num) => { const allNum = 10; return new Promise(resolve => { setTimeout(() => { if (num <= allNum) { resolve([num]) } else { resolve(false) } }, 100) }) } const getList = (num = 1) => { return new Promise((resolve, reject) => { api(num).then(res => { if (res) { resolve(res); } else { reject({ currentPage: num, error: { code: 9876, message: 'can not find the page.' } }); } }) }) } const getWholeList = (num = 1, list = []) => { return new Promise(resolve => { getList(num).then(res => { resolve(getWholeList(num + 1, [...list, ...res])) }).catch(e => { if (e.error.code === 9876) { resolve(list) } }) }) } getWholeList().then(res => { console.log('完成', res) }) ``` |
14
mcfog 2017-03-13 12:53:41 +08:00 via Android
@iugo 错误逻辑有点乱,且只处理了预料中的异常,另外, promise 的生成应该只出现一次就够了,在 new Promise 里面嵌套其他拿 Promise 的方法是典型的反 Promise 模式
|
15
mcfog 2017-03-13 13:31:15 +08:00 1
|