我正在尝试学习如何使用发电机和产量,所以我尝试了以下但是它似乎没有起作用。
我正在使用以下函数,其中包含2个异步调用:
var client = require('mongodb').MongoClient;
$db = function*(collection, obj){
var documents;
yield client.connect('mongodb://localhost/test', function*(err, db){
var c = db.collection(collection);
yield c.find(obj).toArray(function(err, docs){
documents = docs;
db.close();
});
});
return documents.length;
};
然后拨打电话原始电话,我这样做:
var qs = require("querystring");
var query = qs.parse("keywords[]=abc&keywords[]=123");
var total = $db("ads", {"details.keywords": {$in: query["keywords[]"]}});
console.log(total);
当我在控制台中恢复输出时,我明白了:
{}
我期待像200
这样的数字。我做错了什么?
答案 0 :(得分:8)
对于简短的回答,您正在寻找像 co </ strong>这样的帮手。
var co = require("co");
co(myGen( )).then(function (result) { });
ES6迭代器或定义它们的生成器没有任何本质上的异步。
function * allIntegers ( ) {
var i = 1;
while (true) {
yield i;
i += 1;
}
}
var ints = allIntegers();
ints.next().value; // 1
ints.next().value; // 2
ints.next().value; // 3
.next( )
方法实际上允许您将数据中的发送回迭代器。
function * exampleGen ( ) {
var a = yield undefined;
var b = yield a + 1;
return b;
}
var exampleIter = exampleGen();
exampleIter.next().value; // undefined
exampleIter.next(12).value; // 13 (I passed 12 back in, which is assigned to a)
exampleIter.next("Hi").value; // "Hi" is assigned to b, and then returned
考虑可能会令人困惑,但是当你屈服它时,它就像一个回报声明;左侧尚未分配值... ...更重要的是,如果您已将var y = (yield x) + 1;
括号解析为 之前 < / strong>表达式的其余部分......所以你返回,并且+1被置于保持状态,直到值返回为止
然后当它到达时(通过.next( )
传递),表达式的其余部分将被评估(然后分配到左侧)。
每次通话返回的对象都有两个属性{ value: ..., done: false }
value
是你返回/产生的,done
是它是否在函数结束时命中了实际的return语句(包括隐式返回)。
这是可以用来使这种异步魔法发生的部分。
function * asyncGen ( id ) {
var key = yield getKeyPromise( id );
var values = yield getValuesPromise( key );
return values;
}
var asyncProcess = asyncGen( 123 );
var getKey = asyncProcess.next( ).value;
getKey.then(function (key) {
return asyncProcess.next( key ).value;
}).then(function (values) {
doStuff(values);
});
没有魔力
我没有回复价值,而是回复了承诺
当承诺完成后,我使用.next( result )
将结果推回来,这让我得到了另一个承诺。
当这个承诺解决之后,我会使用.next( newResult )
等来推回,直到我完成。
我们现在知道我们只是等待promises解析,然后在迭代器上用结果调用.next
。
我们必须提前了解迭代器的样子,知道我们何时完成?
不是。
function coroutine (iterator) {
return new Promise(function (resolve, reject) {
function turnIterator (value) {
var result = iterator.next( value );
if (result.done) {
resolve(result.value);
} else {
result.value.then(turnIterator);
}
}
turnIterator();
};
}
coroutine( myGen ).then(function (result) { });
这不完整和完美。 co </ strong>涵盖了额外的基础(确保所有收益率都像承诺一样对待,所以你不要通过传递非承诺价值而放弃......或者允许数组承诺yielding,这将成为一个promise,它将返回该yield的结果数组...或者尝试/捕获promise处理,将错误抛回到迭代器中...是的,try / catch与yield语句完美配合,这样做,多亏了迭代器上的.throw(err)
方法。
这些事情并不难实现,但它们使得这个例子比实际情况更加模糊。
这正是 co </ strong>或其他一些&#34; coroutine&#34;或者&#34;产生&#34;方法对于这些东西来说是完美的。
Express服务器背后的人建立了KoaJS,使用Co作为库,而Koa的中间件系统只需要用.use
方法生成生成器并做正确的事。
从ES7开始,规范很可能会为这个确切的用例添加语言。
async function doAsyncProcess (id) {
var key = await getKeyPromise(id);
var values = await getValuesPromise(key);
return values;
}
doAsyncProcess(123).then(values => doStuff(values));
async
和await
关键字一起使用,以实现与协程包装的promise-Yielding生成器相同的功能,而不需要所有外部样板(并且最终具有引擎级优化) )。
如果您使用 BabelJS 之类的转录程序,今天就可以试试。
我希望这会有所帮助。
答案 1 :(得分:1)
Yield和generator与异步无关,它们的主要目的是生成可迭代的值序列,如下所示:
function * gen() {
var i = 0;
while (i < 10) {
yield i++;
}
}
for (var i of gen()) {
console.log(i);
}
只使用星号(生成器函数)调用函数只会创建生成器对象(这就是您在控制台中看到{}
的原因),可以使用next
函数进行交互。
也就是说,你可以使用生成器函数作为异步函数的模拟,但是你需要一个特殊的运算符,比如co。
答案 2 :(得分:1)
var client = require('mongodb').MongoClient;
$db = function*(collection, obj){
var documents;
yield client.connect('mongodb://localhost/test', function*(err, db){
var c = db.collection(collection);
yield c.find(obj).toArray(function(err, docs){
documents = docs;
db.close();
});
});
return documents.length;
};
var qs = require("querystring");
var query = qs.parse("keywords[]=abc&keywords[]=123");
var total = $db("ads", {"details.keywords": {$in: query["keywords[]"]}});
console.log(total);
原样,total
是$db
生成器函数的迭代器。您可以通过yield
检索其total.next().value
值。但是,mongodb库是基于回调的,因此,它的函数不返回值,因此yield
将返回null。
你提到你在其他地方使用Promises;我建议您查看bluebird,特别是其promisify功能。 Promisification反转回调模型,以便回调的参数现在用于解析promisified函数。更好的是,promisifyAll将转换整个基于回调的API。
最后,bluebird也提供了协程功能;但它的协同程序必须返回承诺。因此,您的代码可能会被重写如下:
var mongo = require('mongodb');
var Promise = require('bluebird');
//here we convert the mongodb callback based API to a promised based API
Promise.promisifyAll(mongo);
$db = Promise.coroutine(function*(collection, obj){
//existing functions are converted to promised based versions which have
//the same name with 'Async' appended to them
return yield mongo.MongoClient.connectAsync('mongodb://localhost/test')
.then(function(db){
return db.collectionAsync(collection);})
.then(function(collection) {
return collection.countAsync();});
});
var qs = require("querystring");
var query = qs.parse("keywords[]=abc&keywords[]=123");
$db('ads',{"details.keywords": {$in: query["keywords[]"]}})
.then(console.log)