资讯专栏INFORMATION COLUMN

流程控制: jQ Deferred 与 ES6 Promise 使用新手向入坑!

tomlingtm / 2404人阅读

摘要:谢谢大大指出的关于中用的不到位的错误,贴上大大推荐的文章中的菜鸟和高阶错误,文章很详细说明了一些使用中的错误和指导。另外更正内容在后面补充。从开始说到异步流程控制,之前用的比较多的是的。

谢谢n͛i͛g͛h͛t͛i͛r͛e͛大大指出的关于Promisecatch用的不到位的错误,贴上大大推荐的文章Promise中的菜鸟和高阶错误,文章很详细说明了一些Promise使用中的错误和指导。另外更正内容在后面补充。

从 jQuery $.Deferred() 开始

说到异步流程控制,之前用的比较多的是jQ的Deferred。那Deferred是个啥呢,不清楚没关系,直接控制台来打印看下:

喔!看得出$.Deferred()后是个对象,其下面有着熟悉的done, fail, always字眼(对,,是不是有点熟悉了呢?没错!如果经常用ajax的话就会经常接触到这些货色)。 当然了,不止这些,还有最最最重要的rejectresolve方法,说到这两个方法,就得引出下Deferred的状态机制了——其实很简单,实例化后用上图中的state方法就可以查看($.Deferred().state()),有三种状态

执行resolve/reject前,返回值是pending

执行了resolve,返回值是resolved

执行了reject,返回值是rejected

直接来试着用下吧!这里我们假设执行一个随机延时的setTimeout的异步操作,setTimeout异步操作结束后,根据延时大小,做出不同回应 ! 代码:

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后还可以更改的(不过里面的数据以第一次为准)!!

操作执行后用donefail方法分别接受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里的回调,我在实际中使用时发现总是在donefail里的回调(假设为同步)执行完毕后后执行的。

金掌银掌仙人掌 掌声有请 ES6 Promise

和上面一样,先打印一下!

可以看到Promise下也有熟悉的resolvereject方法,好像和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
    })

有点被绕晕了吧

我们用一句话来梳理下:

链式调下会有一串thencatch,这些thencatch处理器会按照顺序接受上个处理器所产生的返回值,并且根据传入的状态做出不同响应,要么跳过,要么处理(所以上面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͛大大,荆柯刺秦王

写的很粗糙,有错误的地方希望多多指教!!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79401.html

相关文章

  • 总结javascript基础概念(二):事件队列循环

    摘要:而事件循环是主线程中执行栈里的代码执行完毕之后,才开始执行的。由此产生的异步事件执行会作为任务队列挂在当前循环的末尾执行。在下,观察者基于监听事件的完成情况在下基于多线程创建。 主要问题: 1、JS引擎是单线程,如何完成事件循环的? 2、定时器函数为什么计时不准确? 3、回调与异步,有什么联系和不同? 4、ES6的事件循环有什么变化?Node中呢? 5、异步控制有什么难点?有什么解决方...

    zhkai 评论0 收藏0
  • 面试题:没有es6老项目,如何用jq解决异步的问题?

    摘要:我们都知道提供了异步写法,但是大部分的公司都是写的,那我们如何用来写和异步一样的写法呢这个知道的人不多下面我们就来写写把注意以上关键执行完成已经封装好的员工定义执行完成成功失败返回最终返回使用员工对象这样就可以使用了,其实的前身就是的,封 我们都知道es6提供了promise异步写法,但是大部分的公司都是jq写的,那我们如何用Jq来写和promise异步一样的写法呢?这个知道的人不多下...

    hzx 评论0 收藏0
  • promise/deferred 模式原理分析和实现

    摘要:三模式模式其实包含两部分和。六化在编码的时候,想要用进行异步操作流程控制,就要将当前的异步回调函数封装成。 一、什么是promise/deferred 模式 promise/deferred 模式是,根据promise/A 或者它的增强修改版promise/A+ 规范 实现的promise异步操作的一种实现方式。 异步的广度使用使得回调,嵌套出现,但是一但出现深度的嵌套,就会让codi...

    gclove 评论0 收藏0
  • 一篇文章带你尝试拿下js异步

    摘要:单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这决定了它只能是单线程,否则会带来很复杂的同步问题。小结本身是单线程的,并没有异步的特性。当异步函数执行时,回调函数会被压入这个队列。 走在前端的大道上 本篇将自己读过的相关 js异步 的文章中,对自己有启发的章节片段总结在这(会对原文进行删改),会不断丰富提炼总结更新。 概念 JS 是单线程的语言。 单线程就意味着...

    MartinDai 评论0 收藏0
  • Angular $q 完全指南

    摘要:假设家具厂在一周后做完了这个衣柜,并如约送到了张先生家包邮哦,亲,这就叫做衣柜,也就是已解决。这样,整个异步流程就圆满完成,无论成功或者失败,张先生都没有往里面投入任何额外的时间成本。 如果想使用 $http 或者其他异步操作, 那 $q 是必须要掌握的概念啦. Lets get started! 如何理解$q, deferred object ? 形象的讲解angular中的$q与p...

    U2FsdGVkX1x 评论0 收藏0

发表评论

0条评论

tomlingtm

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<