【问题标题】:Using callbacks appropriate as continuations in javascript在 javascript 中使用适当的回调作为延续
【发布时间】:2020-05-01 00:29:27
【问题描述】:

我对回调很熟悉,我知道什么是同步性,但是在延续传递风格方面我仍然缺少一些东西。我有这个简单的代码示例,我不知道如何基于连续传递样式的回调将其转换为异步代码:

function performOrder() {
  var available, placedOrder;
  available = checkCountOnServer(order.item, order.count);
  if (!available) {
    showNotAvailableMessage(order);
    return;
  }
  placedOrder = checkPayment(order.item, order.count, order.paymentMethod);
  if (askUserForConfirmationWithDialog(placedOrder)) {
    return confirmOrder(placedOrder);
  }
}

我的做法是:

function performOrder() {
  checkCountOnServerC(order.item, order.count, showNotAvailableMessage(order));
  return checkPaymentC(order.item, order.count, askUserConfirmationWithDialogC(placedOrder, confirmOrder(placedOrder));
}

可以假设所有附加了 C 的方法都被修改为接受回调作为延续。

我的问题是:sn-ps 是否等效并确认第二个继续传递样式?

非常感谢您!

【问题讨论】:

    标签: javascript asynchronous callback continuations


    【解决方案1】:

    无论available 的状态如何,您的代码都会并行执行checkCountOnServerCcheckPaymentC。因此它们是不等价的。

    正确的转换将取决于您要采用的样式:node.js 标准回调样式、Promises、successCallback/errorCallback 等。假设您要使用 node.js 标准回调样式:

    function performOrderC(callback){
        checkCountOnServerC(order.item, order.count, function(err, available){
            if (! available ) {
                showNotAvailableMessage(order));
                callback(new Error('Not available'));
                return;
            } 
            checkPaymentC(order.item, order.count, function(err, placedOrder){
                askUserConfirmationWithDialogC(placedOrder, function(){
                    callback(null, confirmOrder(placedOrder)); // this is how you "return" results
               });
           });
        });
    }
    

    Node.js 标准回调样式使用回调的第一个参数将错误向上传递到堆栈。但在 node.js 之前,这并没有被异步函数普遍采用。

    如果上面看起来有点密集(有时称为回调地狱),您可以随时将其重构为不使用匿名函数:

    function checkPaymentHandler (callback) {
        return function (err, placedOrder){
            askUserConfirmationWithDialogC(placedOrder, function(){
                callback(null, confirmOrder(placedOrder)); // this is how you "return" results
            });
        });
    }
    
    function checkCountHandler (callback) {
        return function (err, available) {
            if (! available ) {
                showNotAvailableMessage(order));
                callback(new Error('Not available'));
                return;
            } 
            checkPaymentC(order.item, order.count, checkPaymentHandler(callback));
        }
    }
    
    function performOrderC(callback){
        checkCountOnServerC(order.item, order.count, checkCountHandler(callback));
    }
    

    【讨论】:

    • 非常感谢您!这对我来说已经清楚多了。没有我想采用的特殊风格。我只想用尽可能基本的javascript来理解这种“延续传递风格”。返回callback()和直接调用有什么区别,第一个代码中confirmOrder(placedOrder)的结果是sn-p performOrderC()的返回值吗?
    • 这可能有点离题,但在某种程度上是相关的。当像这样链接承诺时: doSomething().then(...).then(function() { .... return value}); 在这种情况下如何访问最后一个返回值价值。我有种感觉,这些类型的构造并不打算与向上传递的值一起工作,更多的是利用所调用方法的副作用。
    • 关于返回回调,只是用return打断程序流程,不执行下面的代码。我将编辑示例以使其更清晰。
    • 任何语言中异步代码的关键是不要向上传递值,而是向下传递代码。请注意,您要执行的代码是callback,因此您将其传递到值可用的位置/时间。 Promises 只是一种设计模式,它不是向下传递回调,而是返回一个带有.then() 方法的对象(承诺),该方法允许您向下传递回调。使用 ES6 Promise 获得了带有 async/await 的语法糖,它模拟向上传递值,但在引擎盖下,编译器只是重新安排代码以将函数的其余部分传递到 Promise 中
    • 请注意,尽管人们称之为延续传递风格,但它与延续或真正的延续传递风格无关。这只是传递函数——它与 monad 的关系比与 continuation 的关系更大。有趣的是 javascript 实际上具有生成器形式的延续(用* 标记的函数)和yield 关键字,它在promise 前整整两年引入但从未流行(除了在使用延续的Koa 框架中)基于称为蹦床的设计模式)
    猜你喜欢
    • 2012-01-17
    • 2016-11-27
    • 2017-07-15
    • 2012-10-14
    • 2010-09-20
    • 2016-09-30
    • 2016-08-27
    • 2011-10-22
    • 2015-05-26
    相关资源
    最近更新 更多