谢谢n͛i͛g͛h͛t͛i͛r͛e͛
大大指出的关于Promise
中catch
用的不到位的错误,贴上大大推荐的文章Promise中的菜鸟和高阶错误,文章很详细说明了一些Promise
使用中的错误和指导。另外更正内容在后面补充。javascript
说到异步流程控制,以前用的比较多的是jQ的Deferred。那Deferred是个啥呢,不清楚不要紧,直接控制台来打印看下:html
喔!看得出$.Deferred()后是个对象,其下面有着熟悉的done
, fail
, always
字眼(对,,是否是有点熟悉了呢?没错!若是常常用ajax的话就会常常接触到这些货色)。 固然了,不止这些,还有最最最重要的reject
和resolve
方法,说到这两个方法,就得引出下Deferred的状态机制了——其实很简单,实例化后用上图中的state
方法就能够查看($.Deferred().state()
),有三种状态java
执行resolve/reject前,返回值是pendingajax
执行了resolve,返回值是resolvedsegmentfault
执行了reject,返回值是rejectedpromise
直接来试着用下吧!这里咱们假设执行一个随机延时的setTimeout
的异步操做,在setTimeout
异步操做结束后,根据延时大小,作出不一样回应 ! 代码:dom
function log (msg) { console.log(msg); } // 包装一个异步操做 var Async = function () { // 生成一个0到5秒的延迟 var delay = Math.floor(Math.random() * 5); // 建立一个Deffered对象 var dfd = $.Deferred(); // 这里调用一个异步操做 setTimeout(function(){ if (delay <= 2) { // 置dfd状态为resolved dfd.resolve('一切正常!'); } else { // 置dfd状态为rejected dfd.reject('超时了!'); } }, delay * 1000) // 这里要返回Deferred下的promise对象Dererred对象的缘由下面会解释 return dfd.promise(); } Async() .done(function (data) { log(data) // 若是延迟不大于三秒 输出dfd.resolve()中的数据 '一切正常!' }) .fail(function (err) { log(err) // 反之则 输出dfd.reject()中的数据 '超时了!' }) .always(function () { log('执行完毕!'); // 老是输出 '执行完毕!' })
在某个操做开始前建立一个
Deferred
对象,而后执行操做异步操做间可根据状况给dfd执行
relove
或者reject
方法改变状态并传入数据函数最后返回出dfd的对象下的一个promise对象,这里不直接返回dfd对象是由于dfd对象的状态是在第一次resolve或者reject后还能够更改的(不过里面的数据以第一次为准)!!spa
操做执行后用
done
和fail
方法分别接受resolve和reject状态和数据(一一对应)而后执行回调(其实1.8还有个then
方法,接受两个参数,第一个参数为resolve
的回调,第二个为reject
的)
always
是不管resolve
仍是reject
都会执行。
我是一个流水线车间质检工人,就在日常的这样的一天,来了一批玩具熊,嗯,接下来应该是这样的
来了一个检查目标(
$.Dererred()
),这时你还不知道它是好是坏我靠我几十年的新东方炒菜技巧检验产品并给良品贴上了合格标签(
dfd.res* olve(合格标签)
),次品贴上回厂标签* (dfd.reject(回厂标签及缘由)
)而后经过的良品和次品都来到了各自的包装口打好包,不能对里面的标签作更改了!(
dfd.promise()
)去往本身下一个目的地(return dfd.promise
)再而后良品来到了熊孩子手中(
.done()
),次品回到了厂里(.fail()
),最后无论玩具熊到了哪里,其实都会被开膛破肚(.always()
好吧这里有点牵强)
还有值得说一下的是always
里的回调,我在实际中使用时发现老是在done
和fail
里的回调(假设为同步)执行完毕后后执行的。
和上面同样,先打印一下!
能够看到Promise下也有熟悉的resolve
和reject
方法,好像和jQ的Deferred
颇为类似!可是不是少了点什么呢?done
或者fail
之类的流程控制的方法呢??
不急,其实展开prototype
原型上就能够看到挂载着的then
方法了!(像极了jQ1.8后那个then
,不过我以为应该说是jQ来遵循Promise
才对)
Promise其实就是个构造函数,仍是以前的例子,这里咱们分三步走
var Async = function () { // 第一步,新建个promise对象,所需的异步操做在其中进行 var prms = new Promise(function(resolve, reject){ // 生成一个0到5秒的延迟 var delay = Math.floor(Math.random() * 5); // 这里调用一个异步操做 setTimeout(function(){ // 第二步, 根据状况置promise为resolve或者reject if (delay <= 2) { // 置dfd状态为resolved resolve('一切正常!'); } else { // 置dfd状态为rejected reject('超时了!'); } }, delay * 1000) }) // 第三步,返回这个Promise对象 return prms } // 强大的来了 Async() // then接受两个函数分别处理resolve和reject两种状态 .then( function(data) { console.log(data) // 一切正常! }, function(err) { console.log(err) // 超时了!! })
粗粗一看好像和Dererred
不能更像了,,不过细心点的话能够发现咱们在函数里直接返回了prms
这个对象,而不是像以前把包装了一层。。。对!由于Promise
的特性就是一旦第一次赋予了状态后面就没法更改了,这也算省心多了吧。可是问题来了,我为何要选择用Promise
呢??
这么说吧,它是原生的 它是原生的 它是原生的!,还有能够链式链式链式链式调用!,咱们能够把每个then
或者catch
当作一个处理器, 好比这样
Async() // 这里暂时只处理resolve .then(function(data) { console.log(data) // 一切正常! return Promise.resolve('随便什么'); }) // 下一个then处理器接收到上一个处理器发出的数据 .then(function(data2) { console.log(data2) // 随便什么 return Promise.reject('错误数据'); }) ...
对!没看错,其实在then
里面你还能够return
其余的promise
对象传并递数据!更有甚你甚至能够什么都不返回,好比说这样
Async() .then(function(data) { console.log(data) // 一切正常! }) // 上面那个处理器若是不return任何东西 就会默认返回个resolve(undefined) // 而后下面的处理器就会接收到这个resolve(undefined) .then(function(data2) { console.log(data2) // undefined // 虽然没有数据来处理,可是你还能够在这里作一些事情啊,例如 return Promise.reject('错误数据'); }) // 嗒哒,catch就这么登场了,这里用catch处理上个then处理器发出的reject .catch(fucntion(err){ console.log(err) // 错误数据 return '那直接返回个字符串呢?' }) // 上个catch处理器返回了个字符串其实也会被下个处理器接受 // 至关于resolve('那直接返回个字符串呢?') .then(function(data3){ console.log(data3) // 那直接返回个字符串呢? }) // 好,接着咱们来试试在没有返回任何东西的状况下接一个catch处理器 .catch(function(err2){ console.log(err2) // 咱们能够来猜一下上面会输出什么,undefined吗? // 错,其实这里什么都不会输出,由于这个catch接收的是resolve // 但它并不会吞没这个resolve而是选择跳过,例如咱们这里再返回 return Promise.resolve('这个字符串会被跳过') }) // 这里紧接着个then处理器,它接受到的数据呢 // 其实并非上个catch返回的resolve('这个字符串会被跳过') // 而是catch以前那个then处理器默认返回的resolve(undefined) .then(function(data4){ console.log(data4) // undefined })
有点被绕晕了吧
链式调下会有一串then
和catch
,这些then
和catch
处理器会按照顺序接受上个处理器所产生的返回值,而且根据传入的状态作出不一样响应,要么跳过,要么处理(因此上面23行处的catch
处理器被跳过了)
ps: 上面咱们用的then
处理器只有一个函数参数,因此只会处理resolve
状态,若是是两个then
就能够处理reject
了。
----更新于5月11日-----
catch
使用的注意上面一块代码中引出了catch
处理器, 以前觉得 cacth()
是 then(null, ...)
的语法糖, 其实这么说不彻底正确(功能层面上来讲这两个是彻底相同的没错——都是处理reject
和异常),可是到了实际使用中Promise中的菜鸟和高阶错误文章中给出了明确的状况证实,这里贴一下:
首先只处理异常状况,下面两个是等价的
somePromise().catch(function (err) { // 处理异常 }); somePromise().then(null, function (err) { // 处理异常 });
可是,若是不仅是处理异常的下面两种状况下就不同了
somePromise().then(function () { return otherPromise(); }).catch(function (err) { // 处理异常 }); somePromise().then(function () { return otherPromise(); }, function (err) { // 处理异常 });
不够清楚吗?那么若是是这样呢?若是第一个回调函数抛出一个错误
会发生什么?
somePromise().then(function () { throw new Error('这里错了!'); }).catch(function (err) { console.log(err) // 这里错了! :) }); somePromise().then( function () { throw new Error('这里错了'); }, function (err) { console.log(err) // 未知 :( // 并无catch到上面那个Error });
结论就是,当使用 then(resolveHandler, rejectHandler)
, rejectHandler
不会捕获在 resolveHandler
中抛出的错误!
看似这个注意项并不影响日常使用,原文做者也说道:
由于,笔者的我的习惯是从不使用then方法的第二个参数,转而使用 catch() 方法
那么,问题来了,如何正确的使用catch
呢? 其实我没有很好的想明白,但愿指教
,随便抛两个砖
// 1 somePromise() .then(resolveHandler) // 这个catch会处理somePromise或者resolveHandler的异常 .catch(rejectHandler) .then(otherResolveHandler) // 而这个catch呢只会处理resolveHandler的异常 .catch(otherRejectHandler) // 2 somePromise() .then(resolveHandler) .then(otherResolveHandler) // 至于这个catch则会处理somePromise、resolveHandler和otherResolveHandler的异常 .catch(rejectHandler) // 3 somePromise() .catch(console.log.bind(console)) //等价于 .catch(function(err){ console.log(err) })
哈哈哈哈哈哈,仍是好好再去想一想Promise去了,弄明白了再来补充,再次谢谢@n͛i͛g͛h͛t͛i͛r͛e͛大大,荆柯刺秦王
写的很粗糙,有错误的地方但愿多多指教!!