Node.js 中 TimeoutPromise 封装

NodejsLearning

使用 Node.js 的同学, 一定免不了使用 Promise, 说到 Promise, 忍不住想要吹一锅 Node.js 了. Promise 配合 asyncawait, 同步异步随时切换, 完全告别了异步时回调嵌套产生的 ugly code.

我们假设这样一个需求, 一段业务逻辑,连续几个异步操作,非常常见的需求.

  1. 先看下最丑陋的搞法, 一堆回掉嵌套, 找打那种的

     (async function () {
         let data = 'happy_coding';
         setTimeout(() => {
             try {
                 data = `step[1]_${data}`;
                 console.log(data);
             } catch (error) {
                 console.log('error = ', error);
             }
             setTimeout(() => {
                 try {
                     data = `step[2]_${data}`;
                     console.log(data);
                 } catch (error) {
                     console.log('error = ', error);
                 }
                 setTimeout(() => {
                     try {
                         data = `step[3]_${data}`;
                         console.log(data);
                     } catch (error) {
                         console.log('error = ', error);
                     }
                 }, 1000);
             }, 1000);
         }, 1000);
     })();
    
  2. 再看下稍好点的情况

     function execAsync(sth) {
         return new Promise((resolve, reject) => {
             setTimeout(() => resolve(sth), 1000);
         })
     }
     
     (async function () {
         execAsync('happy_coding')
             .then((data) => {
                 let tmp = `step[1]_${data}`;
                 console.log(tmp);
                 return execAsync(tmp);
             })
             .then((data) => {
                 let tmp = `step[2]_${data}`;
                 console.log(tmp);
                 return execAsync(tmp);
             })
             .then((data) => {
                 let tmp = `step[3]_${data}`;
                 console.log(tmp);
                 return execAsync(tmp);
             })
             .catch((error) => {
                 console.log('error = ', error);
             });
     })();
    

把异步操作都装进 Promise, 然后 then...then...catch 就好, 当然, 对于非 js 研发同学, 上面的代码已经很漂亮了. 小夫当年写 Java 的时候, 突然有天出来个 RxJava, 就是上面这样的, 当时写起来那酸爽劲, 现在想起来还有些感动, 然后, 带着这份感动直至遇见 Node.js.

  1. 同步方式写异步

     function execAsync(sth) {
         return new Promise((resolve, reject) => {
             setTimeout(() => resolve(sth), 1000);
         })
     }
    
     (async function () {
         try {
             let data = await execAsync('happy_coding');
             data = `step[1]_${data}`;
             console.log(data);
             data = await execAsync(data);
             data = `step[2]_${data}`;
             console.log(data);
             data = await execAsync(data);
             data = `step[3]_${data}`;
             console.log(data);
         } catch (error) {
             console.log('error = ', error);
         }
     })();
    

以上, 把异步操作都装进 Promise 后, 如果想要同步调用, 那么前面 await 一下就好了, 如果不想让这个异步操作阻塞主流程, 那么去掉 await 就好了. 美不美, 简直美. 这可是面向未来的语法. 哈哈. 我知道的好像 Dart 里面也这样搞, Python 里引用 asyncio 也可以, 但是不向 Node.js 里面这么直接.

好了, 前面只是替 Node.js 吹个牛逼, 下面说正题了.

Node.js 中虽然提供了 Promise, 但是有的业务里不够用. 比如最近我的一个需求中就需要一个带超时时间的 Promise, 怎么搞嘞, 查了些许资料, 然后自己封装了下:

    class TimeoutPromise extends Promise {
        constructor(callback, ms = 30 * 1000) {
            let timeout;
            let wrapperPromise = Promise.race([
                new Promise(callback),
                new Promise((resolve, reject) => {
                    timeout = setTimeout(() => {
                        reject(new Error('PTIMEOUT'));
                    }, ms);
                }),
            ]);
    
            super((resolve, reject) => {
                wrapperPromise.then((data) => {
                    clearTimeout(timeout);
                    resolve(data);
                }).catch((error) => {
                    clearTimeout(timeout);
                    reject(error); // if timeout, reject the `PTIMEOUT` error
                })
            });
        }
    }

我们做下测试:

    (async function () {
        let start = Date.now();
    
        function test() {
            return new TimeoutPromise((resolve, reject) => {
                setTimeout(() => { // 模拟异步耗时操作
                    resolve('finished');
                }, 5000); // 异步任务超时时间设置为 5s
            }, 3000) // Promise 超时时间设置为 3s
        }
    
        try {
            await test(); // 该函数会在 3s 后执行完毕
        } catch (error) {
            console.log(' --> Error = ', error);
        }
        
        console.log('spent: ', (Date.now() - start) / 1000);
    })();

下面是执行结果:

     --> Error =  Error: PTIMEOUT
        at Timeout.setTimeout (/home/modorone/WorkSpace/test/timeout_promise/test04.js:18:28)
        at ontimeout (timers.js:475:11)
        at tryOnTimeout (timers.js:310:5)
        at Timer.listOnTimeout (timers.js:270:5)
    spent:  3.011

需要说明的是, await test() 会在 3s 后得到返回值, 但是 test() 中的异步操作依然会在自己的 timeout, 即 5s 之后完成.

小夫参考了 here. 里面实际上提到了两种实现, 我觉得 Promise.race() 比较美.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 欢迎阅读专门探索 JavaScript 及其构建组件的系列文章的第四章。 在识别和描述核心元素的过程中,我们还分享...
    OSC开源社区阅读 1,123评论 1 10
  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,554评论 1 56
  • 一、Promise的含义 Promise在JavaScript语言中早有实现,ES6将其写进了语言标准,统一了用法...
    Alex灌汤猫阅读 797评论 0 2
  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,654评论 0 5
  • 2018年6月30日,膏肓的我,从很不确切的方向,带着求医的心情,背上康复的希望慢慢的向互加徐行。 行在...
    赫章1916魏勇阅读 333评论 4 1