我在多个地方引用了一个函数connectImpl。我试图通过生成器中介来调用此承诺并将其值同步返回到调用函数。如果我在生成器上调用.next(),它将以挂起状态返回
{ value: { state: 'pending' }, done: false }
我想等待这个生成器的值,直到它不再等待。我已经尝试了多个版本的waitOn来实现这一目标,但我似乎无法使其正常工作。
我愿意接受实施建议。这让我有些慌张。令人惊讶的是,最终创建的promise的日志稍后在执行链中触发 - 在生成器完成之后。我显然错过了一些东西:
let models = null ;
let connectImpl = function() {
console.log('connectImpl')
let orm = setupImpl()
let config = getConfigImpl()
let qInitialize = q.nbind(orm.initialize, orm)
if(models) {
console.log('connectImpl:cached')
return q(models)
} else {
console.log('connectImpl:create')
return qInitialize(config).then(function(m){
console.log('connectImpl:created')
models = m
return models
})
}
}
let waitOn = function(generator){
console.log('waitOn')
let done = false ;
let generatorValue = null
while(!done){
var generatorResult = generator.next()
console.log(generatorResult)
done = generatorResult.done
generatorValue = generatorResult.value
}
return generatorValue
}
let domainImpl = function() {
console.log('domainImpl')
let getConnection = function *() {
console.log('domainImpl:getConnection')
yield connectImpl()
}
var generator = getConnection()
return waitOn(generator)
}
console.log('START')
console.log(domainImpl())
console.log('END')
我可以调用并获取
START
domainImpl
waitOn
domainImpl:getConnection
connectImpl
connectImpl:create
{ value: { state: 'pending' }, done: false }
{ value: undefined, done: true }
undefined
END
connectImpl:created
我能够通过这个函数添加执行connectImpl promise以使用中间件 - 但我似乎无法将其改编为我的上述用例:
let domainMiddlewareImpl = function () {
return function *(next) {
let models = yield connectImpl()
this.request.models = models.collections;
this.request.connections = models.connections;
yield next
};
};
答案 0 :(得分:2)
这看起来很有趣。让我们看看我们如何能够实现承诺。我们的最终目标是写出类似的东西:
waitOn(function*(){
console.log("hello");
yield Q.delay(2000); // a placeholder, your calls in your example
console.log("World"); // this should run two seconds late.
});
这里的问题是你在没有提前等待的情况下让他们屈服。首先,你可以跳到最后准备好'准备好了。解决方案(不要!)和here is a fiddle我们正在制作的内容。让我们通过生成器来实现waitOn:
让我们开始吧:
function waitOn(gen){
}
因此,我们的函数需要一个生成器,我们必须做的第一件事是调用它,因为我们需要执行生成器来获得它的结果:
function waitOn(gen){
let sequence = gen(); // call the generator
}
接下来,我们要将所有内容都包含在Promise中,因为我们的waitOn
会产生承诺并返回承诺自己完成:
function waitOn(gen){
let sequence = gen(); // call the generator
return Promise.resolve(); // this is Q.resolve with Q
}
现在,我们有什么案例:
return
所以我们的基本结构如下:
function waitOn(gen){
let sequence = gen(); // call the generator
return Promise.resolve().then(function cont(value){
let {value, done} = en.next(value); // get the next item
// depending on the case do what's appropriate
});
}
注意解构分配 - 我认为这很好,因为你的代码中也有ES6语句。请注意,由于这是第一次调用,value
未定义,但通常我们希望传递上次调用时的值。现在处理案件:
function waitOn(gen){
let sequence = gen(); // call the generator
return Promise.resolve().then(function cont(value){
let {done, value} = en.next(value); // get the next item
if(done) return value; // return case
if(!value || !value.then) return cont(value); // value case, recurse
return value.catch(e => gen.throw(e)).then(cont); // promise case
});
}
请注意.catch
子句 - 我们将我们的代码从promise返回到生成器,以便它处理,以便我们可以尝试/捕获promise。
那就是它!在9行JavaScript中,我们已经为承诺实现了生成器。现在,对于您的代码,您可以产生任何承诺:
let conn = q.nBind(orm.initialize, orm);
waitOn(function*(){
console.log("Starting")
let handle = yield conn(config);
console.log("Handle created!", handle); // connected here
});
快乐编码并享受协程的力量。在我们获得了乐趣之后 - 值得一提的是,Q已经随Q.async
和其他更新的承诺库(如Bluebird)一起发货(Bluebird有Promise.coroutine
)。如果您正在使用承诺库 - 您可以使用它们。此实现也适用于本机承诺。