ES6异步控制流的承诺模式 [英] ES6 Promise patterns for exotic control flows

查看:248
本文介绍了ES6异步控制流的承诺模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

ES6承诺是伟大的。到目前为止,很容易调整我的想法从
回调成语。我发现它自然鼓励更多的模块化代码,并且
课程错误处理更清楚。



但是我遇到了流情形看起来不像(?)喜欢他们
可以很容易地从nodebacks转换为promise(也许只是这样,但也许我只是盲目的答案)。因为promises对于下一个操作是不可知的(或者如果还有一个操作),使用
似乎很难使用API​​,它不仅仅接受回调,而且 return

最常见的例子是done回调。它显示在像数据库连接,表示返回连接池,但我已经看到它弹出在很多其他地方。

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

cb(conn,iNeedToBeToldWhenIAmDone);
}

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

conn.doLotsOfStuff

/ * stuff!这么多的乐趣!* /

/ * okay conn走了我累了* /

done();
});
});

这样的流动反转显然不是你想要在你的API
开始,但它在那里,你不能真正地避免它有时。使用
回调,您可以将稍后调用内部回调传递给原始的外部
回调。这不是完全导致一个干净的分离的关注,但在
至少它是快速和简单。



有一个基于Promise的方法适合情况喜欢这个?一种方式说,
'这里是解决值 - 但是当链完成,也做这个'? I
怀疑没有什么完全匹配我刚才描述的因为它
是不是真的可能说一个链是完成,但也许我缺少一些
模式,让你接近那个没有混乱...






编辑:根据到目前为止的反馈,我意识到根本没有办法将这样的API包装在真正的promises中,因为你返回的promise将永远不能告诉你任何后续的链接承诺。但你可以伪造它。扭曲的是,结果相当脆弱;它必须假设需要连接对象的唯一然后是紧跟其后的那个。承诺的消费者需要理解它是一次性使用的连接,这是不明显的。因此,我并不真的推荐它在实践中,但为了好奇,这里是一个解决方案隐藏,同时表现为(并最终成为)promise链:

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

//假设这来自一个外部库。它返回一个连接和一个
//回调信号,说明你完成了连接。

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

cb(null,connection,done);
}

//我们的promisey包装器上面的

函数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;
}

//测试它

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

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

控制台打印:




  • 已收到连接。

  • 已完成使用连接!

  • '


    不,原生promise不提供这样的结果设施。我会使用一个资源函数,它需要一个promise-returning回调,该回调做的一切(在一个链中),需要在连接打开时完成。资源管理器函数不会将 iNeedToBeTold 传递给回调,而是遵循promise,并在解析时执行需要完成的操作。

      function manageConnection(cb){
    return getSomeConnection(...)//异步获取连接 - 通过promise当然
    .then ){
    function whenDone(){
    ... //做什么需要做的事
    return result;
    }
    var result = cb(conn);
    return result.then(whenDone,whenDone);
    });
    }

    manageConnection(function(conn){
    return conn.doLotsOfStuff(soMuch)
    .then(function(stuff){
    / * stuff !这么多的乐趣!* /
    });
    })then(...)


    ES6 Promises are great. So far it’s been pretty easy to adjust my thinking from the callback idiom. I’ve found it naturally encourages more modular code, and of course error handling is much clearer.

    But a few times I’ve encountered flow situations that don’t seem(?) like they can be readily translated from nodebacks to promises (and perhaps that’s just that, but maybe I’m just blind to the answers). Since promises are agnostic about the next operation (or if there even is one), it seems pretty tough to use Promises with APIs that don’t just take callbacks, but also return them.

    The most common example that comes to mind is the ‘done’ callback. It shows up in things like database connections to signify ‘return connection to pool’ but I’ve seen it pop up in plenty of other places, too.

    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();
        });
    });
    

    Flow-reversal like this is obviously not something you want to have in your APIs to start with, but it’s out there and you can’t really avoid it sometimes. With callbacks, you can pass the ‘call later’ inner callback to the original ‘outer’ callback. It doesn’t exactly lead to a clean seperation of concerns, but at least it’s quick and simple.

    Is there a Promise-based approach suited to situations like this? A way to say, ‘here’s the resolve value -- but when the chain is complete, also do this’? I suspect there’s nothing that perfectly matches what I just described because it isn’t really possible to say a chain is ‘done’, but maybe I’m missing some pattern that gets you close to that without making a mess...


    Edit: Based on the feedback so far I've realized that there's simply no way to wrap such an API in true promises, because the promise you return will never be able to tell you anything about any subsequent chained promises that piggyback on it. But you can fake it. The twist is that the result is rather brittle; it must assume that the only then which needs the connection object is the one which immediately follows. The consumer of the promise would need to understand that it’s a one-time-use connection, which isn’t otherwise obvious. Therefore I don't really recommend it in practice, but for the sake of curiosity here is a solution that hides the done while behaving as (and ultimately becoming) a promise chain:

    /* 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));
    

    Console prints:

    • Received connection.
    • Finished using connection!
    • Done was called.
    • This should be after connection closes.

    解决方案

    A way to say, ‘here’s the resolve value -- but when the chain is complete, also do this’?

    No, native promises do not provide such a facility. I would go with a resource function that takes a promise-returning callback, the callback does everything (in a chain) that needs to be done while the connection is open. Instead of passing iNeedToBeTold to the callback, the resource manager function observes the promise and does what needs to be done when it resolves.

    function manageConnection(cb) {
        return getSomeConnection(…) // get the connections asynchronously - via a promise of course
        .then(function(conn) {
            function whenDone() {
                … // do what needs to be done
                return result;
            }
            var result = cb(conn);
            return result.then(whenDone, whenDone);
        });
    }
    
    manageConnection(function(conn) {
        return conn.doLotsOfStuff(soMuch)
        .then(function(stuff) {
            /* stuff! so much fun! */
        });
    }).then(…)
    

    这篇关于ES6异步控制流的承诺模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆