我有许多异步方法要执行,我的程序流可以根据每个方法返回而改变很多。下面的逻辑就是一个例子。我无法使用Promises以易于阅读的方式编写它。你会怎么写的?
Ps:欢迎更复杂的流程。
Ps2:is_business是一个预定义的标志,我们说我们是否在写一个"业务用户"或者#34; person user"。
begin transaction
update users
if updated
if is_business
update_business
if not updated
insert business
end if
else
delete business
end if
else
if upsert
insert user
if is_business
insert business
end if
end if
end if
commit transaction
答案 0 :(得分:4)
Promises的好处在于它们在同步代码和异步代码之间进行了简单的比较。为了说明(使用Q库):
同步:
var thisReturnsAValue = function() {
var result = mySynchronousFunction();
if(result) {
return getOneValue();
} else {
return getAnotherValue();
}
};
try {
var value = thisReturnsAValue();
console.log(value);
} catch(err) {
console.error(err);
}
异步:
var Q = require('q');
var thisReturnsAPromiseForAValue = function() {
return Q.Promise(function() {
return myAsynchronousFunction().then(function(result) {
if(result) {
// Even getOneValue() would work here, because a non-promise
// value is automatically cast to a pre-resolved promise
return getOneValueAsynchronously();
} else {
return getAnotherValueAsynchronously();
}
});
});
};
thisReturnsAPromiseForAValue().then(function(value) {
console.log(value);
}, function(err) {
console.error(err);
});
你只需要习惯这样的想法,即返回值总是作为then-callbacks的参数被访问,而链接promises等同于组合函数调用(f(g(h(x)))
)或以其他方式执行函数({{ 1}})。基本上就是这样!当你介绍分支时,事情变得有点棘手,但你可以从这些第一原则中找出应该做些什么。因为then-callbacks接受promises作为返回值,所以你可以通过返回另一个promise 来异步操作异步操作来改变你异步获得的值,该异步操作根据旧的值解析为新值,而父承诺将直到新的结算才解决!当然,你可以从if-else分支中返回这些承诺。
上面示例中说明的另一个非常好的事情是,promises(至少与Promises / A +兼容)以同样类似的方式处理异常。第一个错误"提出"绕过非错误回调并冒泡到第一个可用的错误回调,就像try-catch块一样。
对于它的价值,我认为尝试使用手工制作的Node.js风格的回调来模仿这种行为,而var x2 = h(x); var x3 = g(x2);
库是它自己特殊的地狱:)。
遵循这些准则,您的代码将成为(假设所有函数都是异步并返回promise):
async
答案 1 :(得分:2)
解决方案是@mooiamaduck回答和@Kevin评论的混合。
使用promises,ES6生成器和co库使代码更加清晰。我在阅读postgresql节点库示例(pg)时找到了一个很好的例子。在下面的示例中,pool.connect
和client.query
是返回Promises的异步操作。我们可以在生成if/else
之后轻松添加result
,然后进行更多异步操作,使代码看起来像同步。
co(function * () {
var client = yield pool.connect()
try {
yield client.query('BEGIN')
var result = yield client.query('SELECT $1::text as name', ['foo'])
yield client.query('INSERT INTO something(name) VALUES($1)', [result.rows[0].name])
yield client.query('COMMIT')
client.release()
} catch(e) {
// pass truthy value to release to destroy the client
// instead of returning it to the pool
// the pool will create a new client next time
// this will also roll back the transaction within postgres
client.release(true)
}
})