用于奇异控制流的 ES6 Promise 模式

2023-12-23

ES6 Promise 很棒。到目前为止,调整我的想法很容易 回调习惯用法。我发现它自然地鼓励更多的模块化代码,并且 当然错误处理更加清晰。

但有几次我遇到了看起来不像(?)的心流情况 可以很容易地从节点返回到承诺(也许就是这样,但也许我只是对答案视而不见)。由于承诺是不可知的 关于下一个操作(或者如果有的话),它似乎很难使用 API 的 Promise 不仅接受回调,还接受return them.

我想到的最常见的例子是“done”回调。它出现在数据库连接之类的东西中,表示“将连接返回到池”,但我也看到它出现在很多其他地方。

function getSomeStupidConnection(cb) {
    var conn = /* ... */;
    var iNeedToBeToldWhenIAmDone = function() { /* ... */ };

    cb(conn, iNeedToBeToldWhenIAmDone);
}

getSomeStupidConnection(function(conn, done) {
    /* ... */

    conn.doLotsOfStuff(function(soMuchStuff) {

        /* stuff! so much fun! */

        /* okay conn go away I’m tired */

        done();
    });
});

像这样的流程反转显然不是您希望在 API 中出现的内容 首先,但它就在那里,有时你无法真正避免它。和 回调,您可以将“稍后调用”内部回调传递给原始“外部” 打回来。它并不完全导致关注点的彻底分离,但在 至少它既快速又简单。

是否有适合这种情况的基于 Promise 的方法?一种说法是, “这是解析值——但是当链完成时,也执行此操作”?我 怀疑没有什么与我刚才描述的完全相符,因为它 确实不可能说一条链“完成”了,但也许我遗漏了一些 让你接近它而不弄乱的模式......


编辑:根据到目前为止的反馈,我意识到根本没有办法将这样的 API 包装在真正的 Promise 中,因为您返回的 Promise 将永远无法告诉您有关任何后续链式 Promise 的信息。但是你can假装。问题在于,结果相当脆弱。它必须假设唯一的then需要连接对象的是紧随其后的对象。承诺的使用者需要了解这是一次性连接,否则这一点并不明显。因此,我在实践中并不推荐它,但出于好奇,这里有一个隐藏了的解决方案done同时表现为(并最终成为)承诺链:

/* jshint node: true, esnext: true */
'use strict';

// Assume this comes from an external library. It returns a connection and a
// callback to signal that you are finished with the connection.

function getConnectionExternal(cb) {
    let connection = 'Received connection.';
    let done = () => console.log('Done was called.');

    cb(null, connection, done);
}

// Our promisey wrapper for the above

function getConnection() {
    let _done;

    let promise = new Promise((resolve, reject) => {
        getConnectionExternal((err, connection, done) => {

            if (err) return reject(err);

            _done = (val) => {
                done();
                return val;
            };

            resolve(connection);
        });
    });

    let _then = promise.then.bind(promise);

    promise.then = (handler) => _then(handler).then(_done, _done);

    return promise;
}

// Test it out

getConnection()
    .then(connection => {
        console.log(connection);

        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('Finished using connection!');
                resolve('This should be after connection closes.');
            }, 200);
        });
    })
    .then(msg => console.log(msg))
    .catch(err => console.error(err));

控制台打印:

  • 已收到连接。
  • 使用连接完毕!
  • 完成被称为。
  • 这应该是在连接关闭之后。

详细阐述 Bergi 的解决方案,这称为处置器模式。它以多种形式存在于多种语言中——with在Python中,using在 C# 中和try(){与 Java 中的资源。有些语言通过析构函数以这种方式本地处理作用域中的资源,例如 C#。

一般的想法是用一个范围来封装一个值的生命周期。在你的情况下是数据库连接。这比打电话要整洁得多done在回调中,因为更容易忘记调用done这会留下打开的连接和资源泄漏。同步地它看起来像:

function scope(cb){
     try{ 
         var conn = getConnection(...);
         return cb(conn);
     } finally {
         conn.release();
     }
}

承诺版本并没有太大不同:

function conn(data){
    var _connection;
    return getConnection().then(function(connection){
        _connection = connection; // keep a reference
        return data(_connection); // pass it to the function
    }).then(function(val){
         // release and forward
         _connection.release(); // if release is async - chain
         return val; 
    }, function(err){
         _connection.release();
         throw err; // forward error
    });
});

哪个会使用:

conn(function(db){
    return db.query("SELECT * FROM ...");
}).then(function(result){ // handle result
    // connection is released here
});
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用于奇异控制流的 ES6 Promise 模式 的相关文章

  • 使用 sinon 对 ES6 原型方法进行存根

    我在使用 Sinon 存根超类的原型方法时遇到问题 在下面的示例中 我将对超类方法 GetMyDetails 的调用进行存根处理 如下所示 我确信有更好的方法 actor sinon stub student proto proto Get
  • 等待多个异步调用完成后再继续

    因此 我有一个加载页面 并通过 jquery get 发出多个请求以使用其值填充下拉列表 function LoadCategories Category LoadPositions Position LoadDepartments Dep
  • Map对象,在单个循环中删除元素

    在某些条件下 在单个循环中删除 Map 对象的元素的简洁有效的方法是什么 迭代地图的条目 可以做到这一点 但在每次迭代时调用条目 看起来效率不高 let name value map new Map One 1 Two 2 Three 3
  • 如何创建自定义元素扩展类的新实例

    我正在尝试以下示例谷歌开发者网站 https developers google com web fundamentals getting started primers customelements extendhtml我收到错误 Typ
  • 通过多个回调优雅地传递“点击事件”

    当未登录的用户单击给定的按钮时 我想停止该事件 收集他的 oauth 收集他的电子邮件 如果我没有 然后执行该事件 我想用 javascript 来做所有事情 因为这会让事情变得更加简单 这就是我执行它的方式 我有两个问题 有没有更优雅的方
  • Node.js 检测两个猫鼬查找何时完成

    我正在尝试使用自动完成功能初始化两个输入library https www devbridge com sourcery components jquery autocomplete 当我加载页面时 我将触发 Ajax 来初始化两个输入文本
  • 如何使用 ES5 扩展 ES6 类? [复制]

    这个问题在这里已经有答案了 这样做的原因很复杂 但归根结底是因为 Flow 不理解 mixin 或任何其他修改 ES6 类原型的方法 所以我又回到了 ES5 但我不知道如何在不使用 ES6 的情况下调用 ES6 类的构造函数new clas
  • 为什么 JavaScript ES6 不支持多构造函数类?

    我想编写我的 Javascript 类 如下所示 class Option constructor this autoLoad false constructor key value this key value constructor k
  • 关闭捕获上下文 Swift

    当我尝试更改闭包中的变量时出现此错误 A C function pointer cannot be formed from a closure that captures context 是否有解决方法或者仍然可以更改闭包内的变量 My C
  • 未调用 Ajax 回调。如何解决这个问题?

    我再次来这里寻求您的建议 我有一些 AJAX 调用 用于从我的编辑器 PHP 在线编辑器 运行代码 大家可以来看看my site http web guru99 com 我有一些 ajax 调用来发送编辑器的数据并接收输出 问题 for 循
  • 仅当对象为真时才解构对象

    假设我想像这样解构我的函数参数 const func field subField gt subField 如果字段是 如何防止抛出错误undefined or null 您可以使用默认值 const func field subField
  • 运行“gulp”命令时出现“错误 [ERR_REQUIRE_ESM]”

    我是新来的Gulp并尝试自动化一些任务 这是我的环境设置 npm version 8 1 0 node version 17 0 1 gulp CLI version 2 3 0 and gulp version 4 0 2 这是我的gul
  • React - 无法读取未定义的属性[重复]

    这个问题在这里已经有答案了 通常 当我单击子组件中的菜单项时 它会调用 this handlesort 这是一个本地函数 处理排序从我的父组件中获取 onReorder 属性 onReorder 调用名为 reOrder 的本地函数 它设置
  • 从 ES6 箭头函数返回对象文字

    如果有人解释为什么在 UpdatedPosts 中我们应该返回一个对象 而在一个对象内我们应该再次返回它 我将不胜感激 为什么我们可以只做这种和平的代码 gt const UpdatedPosts posts map 数据 gt 数据 作者
  • 等待 Promise 和嵌套 then 完成

    我从这样的函数返回一个承诺 resultPromise dgps utils save opportunity api Opportunity Save opportunity dirtyFlag then function self ch
  • ES6,如何在一行中导出导入的模块?

    如果可能的话 我想要以下内容 但只有一行 import Module from Module Module export Module 我尝试了以下方法 但似乎不起作用 export Module from Module Module ex
  • 获取 android 回调“外部”变量的值

    有一个字符串message作为接口方法中的参数 public class Home extends AppCompatActivity private String globalStringResult null Override prot
  • 如何修复“无法读取 null 的属性‘addEventListener’”错误[重复]

    这个问题在这里已经有答案了 我正在从 Mozilla 开发者网络 MDN 学习 javascript 我在猜数字游戏中遇到错误 该错误在第 46 行显示 Uncaught TypeError Cannot read property add
  • 为持久任务创建 REST API 的最佳方法?

    假设我有 2 台服务器 第一个是提供一些计算的服务 该计算可以持续很长时间 几分钟到几小时 第二个服务器将使用此服务来计算一些数据 我正在尝试为第一台服务器设计一个 REST API 到目前为止一切顺利 但我想听听一些关于如何在长期任务完成
  • 如何修改Open Shift中的节点启动命令?

    我使用 ES6 和 babel node 来创建我的应用程序 并且我要求我的应用程序以命令启动babel node app js 此命令列在我的 package json 中的 script start 中 因此命令 npm start 运

随机推荐

  • spring data jpa多重排序

    我正在使用 spring data jpa 和 JQGrid 我需要基于多个排序参数的响应 我尝试使用 sort parameter column a column b 和 sort order asc 但出现异常 pojo 中没有找到属性
  • 禁用 PDFView 上的缩放 (iOS)

    如何禁用 PDFView 的缩放功能 我已经尝试过解决方案here https stackoverflow com questions 54829190 how to disable zoom in and zoom out in pdfv
  • 为什么 object 不默认为零?

    在 Delphi 中 从 TObject 继承的变量的记录行为是默认值 nil 然而 我遇到过一种情况 情况并非如此 通过 IDE F9 运行以下代码示例会产生混合结果 var objTemp TMemDataSet begin if ob
  • 有没有办法使用 Django REST 框架中的可浏览 API 上传文件?

    我需要测试一个上传文件的 API 如何通过可浏览的 API 执行此操作 可浏览的 API 页面如下所示 有没有办法使用这个接口上传文件 views py class TrainingData APIView API for getting
  • 从图像创建视频会对某些图像格式产生黑屏视频[重复]

    这个问题在这里已经有答案了 我正在使用下面的命令从图像创建视频 该命令适用于大多数图像 但对于 png 图像 创建的视频无法播放 我只是得到黑屏 String command new String y f concat safe 0 i s
  • 找不到org.springframework.util.ClassUtils.getMethod并且无法初始化DefaultConversionService

    运行我的应用程序后 它会在控制台中显示以下错误 我研究了这个问题 但它answer https stackoverflow com questions 20851236 spring mvc http status 500 servlet
  • Django 模型管理器是否需要 using=self._db

    在使用 Django 用户模型时 我注意到模型管理器包括using self db作用于数据库时的参数 如果我只使用单个数据库 这有必要吗 什么是using self db除了指定数据库之外 还可以执行其他操作 如果添加另一个数据库 这是否
  • gdb 回溯

    我刚刚尝试过使用 gdb回溯Linux http www backtrack linux org 我必须说这太棒了 我想知道 backtrack 中的 gdb 是如何配置为这样的 当我设置断点时 会打印所有寄存器值 堆栈的一部分 数据部分的
  • 适用于 Android 的 Google 环聊 API

    您知道如何将 Google Hangout 集成到 Android 应用程序中吗 我没有看到任何当前可用的 API 使用 WebView 是一种选择吗 目前没有任何内容 我不相信 WebView 版本会起作用 因为环聊需要谷歌安装的相机插件
  • Python Fizzbuzz 循环问题

    我已经搜索了大约一个小时的答案 似乎大多数人都以与我不同的方式编码 fizzbuzz 然而 在尝试了一切方法来找出为什么这个简单的代码不起作用之后 我感到非常沮丧 谁能指出我确定遇到的简单问题 代码运行但只返回值 1 def fizzbuz
  • Android L Nexus 5 上的材料设计

    我的应用程序在 Android L 上崩溃 但在 Android Kitkat 上运行 布局上也没有 RippleDrawable 这是异常和布局 XML 布局
  • ocaml 命令行找不到“topfind”

    我已经安装了顶级 并且有 OCAML TOPLEVEL PATH 设置 export OCAML TOPLEVEL PATH Users smcho opam system lib toplevel 我检查该目录是否存在 并且有一个文件to
  • 在 strpos() 的字符串中使用正则表达式

    我想让脚本搜索 open email msg 不同的电子邮件将有不同的信息 但格式相同 如下所示 我并没有真正使用正则表达式太多 但我想做的是每当我用它来搜索字符串时 它都会搜索 标题 标题数据 类别 类别数据 我问因为我不认为类似的事情
  • 无法找到 Python PIL 库。Google App Engine

    完美安装了Google App Engine SDK Python 2 6 想要进入图像并在本地进行测试 已安装 PIL 安装了Python 然后运行了PIL安装 这次成功了 事情看起来不错 但尝试进行本地主机图像处理 给出 NotImpl
  • Node Puppeteer, page.on( "request" ) 抛出“请求已处理!”

    我在用着puppeteer extra https www npmjs com package puppeteer extra和 node js 来迭代多个 url 我试图拦截一些资源类型以在每次迭代时加载 并收到以下错误 PS C Use
  • ios 中的阴影

    iOS 中如何去掉物体的阴影 My object is UIImageView and i want to drop an elliptical shadow Please refer image for reference 最好使用另一张
  • 使一个流的输出成为另一个流的输入的最佳方法是什么

    我想知道除了使用字节缓冲区和循环之外 是否有更好 内置的方法从一个流读取并将其写入另一个流 在 NET 中 通常 这样做是为了将转换应用于流并将其继续移动 在本例中 我正在加载一个文件 将其放入 deflate 流并将其写入文件 为简单起见
  • AMD(特别是 RequireJs)如何处理跨多个模块的依赖关系

    我有一个调用 require 的主初始化脚本 其中一个依赖项是实用程序框架 但我通过 require 指定的其他一些模块本身也已将此框架定义为依赖项 例如 init js require module a module b module c
  • 删除了方法的 C++ 类可以轻松复制吗?

    我希望类 B 继承类 A 的除少数方法之外的所有方法 假设它是可简单复制的 并且仍然可简单复制 在 C 11 中我可以删除方法 举个例子 class A trivially copyable private stuff here publi
  • 用于奇异控制流的 ES6 Promise 模式

    ES6 Promise 很棒 到目前为止 调整我的想法很容易 回调习惯用法 我发现它自然地鼓励更多的模块化代码 并且 当然错误处理更加清晰 但有几次我遇到了看起来不像 的心流情况 可以很容易地从节点返回到承诺 也许就是这样 但也许我只是对答