zoukankan      html  css  js  c++  java
  • 中断或取消Promise链的可行方案

    ES6标准引入的异步编程解决方案Promise,能够将层层嵌套的回调转化成扁平的Promise链式调用,优雅地解决了“回调地狱”的问题。
    当Promise链中抛出一个错误时,错误信息沿着链路向后传递,直至被捕获。利用这个特性能跳过链中函数的调用,直至链路终点,变相地结束Promise链。

     1 Promise.resolve()
     2     .then(() => {
     3         console.log('[onFulfilled_1]');
     4         throw 'throw on onFulfilled_1';
     5     })
     6     .then(() => {  // 中间的函数不会被调用
     7         console.log('[onFulfilled_2]');
     8     })
     9     .catch(err => {
    10         console.log('[catch]', err);
    11     });
    12 // => [onFulfilled_1]
    13 // => [catch] throw on onFulfilled_1

    然而,若链路中也对错误进行了捕获,则后续的函数可能会继续执行。

     1 Promise.resolve()
     2     .then(() => {
     3         console.log('[onFulfilled_1]');
     4         throw 'throw on onFulfilled_1';
     5     })
     6     .then(() => {
     7         console.log('[onFulfilled_2]');
     8     }, err => {     // 捕获错误
     9         console.log('[onRejected_2]', err);
    10     })
    11     .then(() => {   // 该函数将被调用
    12         console.log('[onFulfilled_3]');
    13     })
    14     .catch(err => {
    15         console.log('[catch]', err);
    16     });
    17 // => [onFulfilled_1]
    18 // => [onRejected_2] throw on onFulfilled_1
    19 // => [onFulfilled_3]

    解决方案
    Promise的then方法接收两个参数:
    Promise.prototype.then(onFulfilled, onRejected)
    若onFulfilled或onRejected是一个函数,当函数返回一个新Promise对象时,原Promise对象的状态将跟新对象保持一致,详见Promises/A+标准。
    因此,当新对象保持“pending”状态时,原Promise链将会中止执行。

     1 Promise.resolve()
     2     .then(() => {
     3         console.log('[onFulfilled_1]');
     4         return new Promise(()=>{}); // 返回“pending”状态的Promise对象
     5     })
     6     .then(() => {                   // 后续的函数不会被调用
     7         console.log('[onFulfilled_2]');
     8     })
     9     .catch(err => {
    10         console.log('[catch]', err);
    11     });
    12 // => [onFulfilled_1]

    主要问题是:链式调用时,想在下层返回resolve的情况下,需要在中途得到某种resolve结果就终止调用链。(PS:下层可能是调用其他人编写模块,比如参数不对,它仍会返回resolve,出现错误才会reject,本身下层Promise 返回reject是可以打断调用链的)

    下面有个链式调用Promise的测试函数

     1 const promiseFun = function(param1){
     2     return new Promise((resolve, reject)=>{
     3         resolve(param1);
     4     });
     5 }
     6 const promiseTest = function(param1, param2){
     7     return new Promise((resolve, reject)=>{
     8         promiseFun(1).then((number)=>{
     9             console.info(`fun1 result:${number}`);
    10             return promiseFun(2);
    11         }).then((number)=>{
    12             console.info(`fun2 result:${number}`);
    13             return promiseFun(3);
    14         }).then((number)=>{
    15             console.info(`fun3 result:${number}`);
    16             return promiseFun(4);
    17         }).then((number)=>{
    18             console.info(`fun4 result:${number}`);
    19         }).catch((err)=>{
    20             console.info(`promiseTest error:${err}`);
    21         });
    22     });        
    23 }
    24 promiseTest('1','2').then((number)=>{
    25     console.info(`promiseTest:${number}`);
    26 }).catch((err)=>{
    27     console.info(`promiseTest failed:${err}`);
    28 });

    现在遇到的一个问题是,比如我们在fun2时,我们调用reject 想终止该链式调用,但实际的结果是仍然会跑到

    1 console.info(`fun3 result:${number}`)及console.info(`fun4 result:${number}`)。

    PS: 这种想法本身就很奇怪,因为我们调用reject 只是影响promiseTest 下创建那个Promise的状态。也就是调用reject后promiseTest 这个函数会触发onRejected状态。而链式Promise调用状态是由下层Promise对象的状态决定。

     1 promiseFun(1).then((number)=>{
     2             console.info(`fun1 result:${number}`);
     3             return promiseFun(2);
     4         }).then((number)=>{
     5             console.info(`fun2 result:${number}`);
     6            if(number === 2){
     7                 reject(number)
     8             }
     9             else{
    10                 return promiseFun(3);
    11             }
    12         }).then((number)=>{
    13             console.info(`fun3 result:${number}`);
    14             return promiseFun(4);
    15         }).then((number)=>{
    16             console.info(`fun4 result:${number}`);
    17         }).catch((err)=>{
    18             console.info(`promiseTest error:${err}`);
    19         });

    2.2 原因
    Promise的then方法接收两个参数:
    Promise.prototype.then(onFulfilled, onRejected)
    若onFulfilled或onRejected是一个函数,当函数返回一个新Promise对象时,原Promise对象的状态将跟新对象保持一致。
    来自:https://promisesaplus.com/

    解释下原因:
    为什么我们链式调用中reject没有作用?
    因为reject仅仅改变的是外层包的promiseTest 返回Promise状态。而链式调用的状态是由promiseFun 返回的状态决定, 而在第二个链式调用then时我们调用reject改变了promiseTest 那种Promise的状态进而使promiseTest触发onRejected状态打印 promiseTest failed:${err},而链式第二个链式调用中本身没做修改链式调用的状态,所以第三个链式继承了第一个链式调用返回的Promise的resolve状态,导致链式调用继续向下运行。

    2.3 解决方案
    而针对上面的问题,我们想要在resolve的情况下,中断或终止链式调用。
    还是基于Promise的特点:原Promise对象的状态将跟新对象保持一致。

    我们仅需要在链式调用中,返回一个pending 状态或reject状态的Promise对象即可。后面then 所有resolve(onFulfilled)的处理函数就都不会跑到了。即:

    1 return (new Promise((resolve, reject)=>{}));//返回pending状态
    2 return (new Promise((resolve, reject)=>{reject()}));//返回reject状态 会被最后catch捕获。

    在测试代码中就想这样

    then((number)=>{
                console.info(`fun2 result:${number}`);
               if(number === 2){、
             return (new Promise((resolve, reject)=>{}));
                    reject(number)
                }
                else{
                    return promiseFun(3);
    }
  • 相关阅读:
    设计模式系列之中介者模式(Mediator Pattern)——协调多个对象之间的交互
    设计模式系列之外观模式(Facade Pattern)——提供统一的入口
    设计模式系列之装饰模式(Decorator Pattern)——扩展系统功能
    设计模式系列之组合模式(Composite Pattern)——树形结构的处理
    设计模式系列之工厂模式三兄弟(Factory Pattern)
    设计模式系列之建造者模式(Builder Pattern)——复杂对象的组装与创建
    设计模式系列之原型模式(Prototype Pattern)——对象的克隆
    动态追踪技术之SystemTap
    一次内核 crash 的排查记录
    LLVM Coding Standards
  • 原文地址:https://www.cnblogs.com/xfcao/p/12084340.html
Copyright © 2011-2022 走看看