generator 如何用 yield 获取 generator 的回调
发布于 9 年前 作者 Hanggi 4974 次浏览 最后一次编辑是 8 年前 来自 问答

目前场景非常简单,是在router文件中定义了一个generator function,用 yield 调用model文件中的一个方法, 而这个方法本身也是generator function,方法中yield 调用了一些第三方方法。全程thunkify处理。

用readFile模拟代码: 假设这是router中的一个路由,访问model层等待数据返回

var generator1 = function *(next) {
	var thunk = thunkify(generator2);
	// 程序会在这个地方停住,无法继续前进。
	var res = yield thunk(__dirname + '../public/text');
	this.body = res;
}

假设这是model层里的函数,操作数据库获得数据,

var generator2 = function *(fd, callback) {
	var thunk = thunkify(fs.readFile);
	var res = yield thunk(fd);
	callback(null, res);
}

程序在注释处停止,没有任何响应,没有报错,程序也在运行,页面在不停等待。 经测试 generator2 函数改写成普通的回调函数,程序就会正常运行。

var generator2 = cb = function (fd, callback) {
    fs.readFile(fd, function (err, data) {
        callback(null, data);
    });
}

不知道是方法不对,还是思路不对,yield就是进不去generator。 写过yield的人应该看过系统提示,可以yield function, generator, array, 等等 不知有没有人遇到过。

12 回复

我用那个thunkify从来没有对过 自豪地采用 CNodeJS ionic

我自己重新写了一个bluebird的function转promise的函数 你可以到我的github上面去看看 要是觉得麻烦 就直接用bluebird的fromNode方法 在yield 自豪地采用 CNodeJS ionic

@wenshiqi0 谢谢,我去看看。

thunkify

turn a regular node function into one which returns a thunk

而你把generator传进去,所以得不到期望的结果

var thunk = thunkify(generator2);

为什么会停,下面我的分析 上面那一句,简单地展开大概是下面的形式(粗略表示,不严谨)

var thunk = function(){
   var args=Array.prototype.slice.call(arguments);
   return function(callback){
           args.push(callback);
           generator2.apply(null,args);
   }
}

所以

var res = yield thunk(__dirname + '../public/text');

大约相当于

var res = yield function(callback){   
    generator2.apply(null, [_dirname + '../public/text',callback]); 
};

yield一个函数的时候,co会把它放promise里执行

function thunkToPromise(fn) {
  var ctx = this;
  return new Promise(function (resolve, reject) {
    fn.call(ctx, function (err, res) {
      if (err) return reject(err);
      if (arguments.length > 2) res = slice.call(arguments, 1);
      resolve(res);
    });
  });
}

由于上面的callback一直没有机会执行,所以co包装的promise一直没有结果,所以一直等待

你试试把generator2改成类似这样,然后在generator1里面直接yield不需要thunkify。这里基本的错误是把generator加了回调。generator的一个作用是可以在里面像写同步一样组织异步流程。

var generator2 = function *(fd) { var thunk = thunkify(fs.readFile); var res = yield thunk(fd); return res; }

别混用, 头快炸了

@magicdawn 我想的是既然是用generator处理异步回调,到model层又要用回callback,那就不彻底了。。。

@Hanggi 我用co跑是通过的。实在不行你加上try catch看能不能抓到错误:

var fs = require(‘fs’); var co = require(‘co’); var thunkify = require(‘thunkify’);

var generator2 = function*(fd) { try{ var thunk = thunkify(fs.readFile); var res = yield thunk(fd, ‘utf8’); return res; } catch(e) { console.log(e); return e; } };

var generator1 = function*(next) { var res = yield generator2(__dirname + ‘/test.txt’); console.log(res); };

co(generator1());

另外,你的fd路径是不是有问题呢? __dirname + ‘…/public/text’

@chrischjh 重新试了下,运行成功了。

@Hanggi 我知道,koa跟co在异步管理上类似的,实际上koa应该背后是使用co来组织异步流程的。我相信你用try catch是可以找出错误的,多半是文件路径写的有问题。

如果你非要在那里用generator. 那应该是这样

var generator1 = function *(next) {
    // 由于generator2已经是generator, 所以不必thunkify, 直接 yield
	// 由于你想得到yield表达式的值,所以 generator2里面应该把结果 return
    var res = yield generator2(__dirname + '../public/text');
    this.body = res;
}
// 里面用return
var generator2 = function *(fd) {
    var thunk = thunkify(fs.readFile);
    var res = yield thunk(fd);
    return res;
}

@William17 是的,正常输出了,谢谢。

回到顶部