了解节点JS发电机以fs模块 [英] Understanding Node JS Generators with fs Module

查看:138
本文介绍了了解节点JS发电机以fs模块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直对节点JS很兴奋一段时间。我终于决定屈服来写一个测试项目,了解节点的最新构建和谐发电机。

下面是我非常简单的测试项目:

https://github.com/kirkouimet/project-node

要运行我的测试项目,你可以很容易地拉离Github上的文件,然后运行它:

 节点--harmony App.js

下面是我的问题 - 我似乎无法得到节点的异步fs.readdir方法与发电机内联运行。其他项目在那里,如银河暂停似乎能够做到这一点。

下面是code的我需要修复块。我希望能够实例类型的文件系统对象,并调用.LIST()方法就可以了:

<一个href=\"https://github.com/kirkouimet/project-node/blob/4c77294f42da9e078775bb84c763d4c60f21e1cc/FileSystem.js#L7-L11\">https://github.com/kirkouimet/project-node/blob/4c77294f42da9e078775bb84c763d4c60f21e1cc/FileSystem.js#L7-L11

 文件系统= Class.extend({    构建:功能(){
        this.currentDirectory = NULL;
    },    清单:功能*(路径){
        VAR列表=产量NodeFileSystem.readdir(路径);        返回列表;
    }});

我需要做一些事情的时间提前到节点的fs.readdir转换成发电机?

一个重要的提示,我解析所有类函数,因为他们创建的。这让我处理发生器功能不同于普通的功能:

<一个href=\"https://github.com/kirkouimet/project-node/blob/4c77294f42da9e078775bb84c763d4c60f21e1cc/Class.js#L31-L51\">https://github.com/kirkouimet/project-node/blob/4c77294f42da9e078775bb84c763d4c60f21e1cc/Class.js#L31-L51

我已经真的难倒这个项目。喜欢的任何帮助!

下面就是我要完成的:


  1. 重型使用类与约翰Resig的JavaScript类支持,继承修改后的版本

  2. 使用生成器来获取节点的股票异步调用在线支持

修改

我试图实现你的榜样作用,我遇到了一些麻烦。

 列表:功能*(路径){
    无功名单= NULL;    VAR whatDoesCoReturn = CO(功能*(){
        列表=产量READDIR(路径);
        的console.log(名单); //这显示文件的数组(好!)
        返回列表; //只是我的猜测,应该共同获得这个回来,这不
    })();
    的console.log(whatDoesCoReturn); //这个返回undefined(伤心)    //我需要使用`list`就在这里    返回列表; //这将返回为空
}


解决方案

首先,它有一个很好的模式在你的生成正是头是非常重要的。发电机的功能是返回一个发电机对象的函数,而发电机对象将通过收益率步骤您拨打的生成函数中的语句。接下来( )就可以了。

由于描述,你应该注意到异步行为没有被提及。对自己的发电机的任何行动是同步的。您可以立即运行到第一个收益率,然后做一个的setTimeout ,然后调用。接下来()来进入下一个收益,但它是的setTimeout 引起异步行为,而不是发电机本身

因此​​,让我们投这 fs.readdir 的光。 fs.readdir 是一个异步函数,并在自己的发电机使用它不会有任何效果。让我们来看看你的例子:

 功能*读取(路径){
    返回产量fs.readdir(路径);
}VAR根=读(路径);
//根现在是一个生成器对象。变种第一= gen.next();
//这相当于第一= fs.readdir(路径);
//这意味着第一===未定义自fs.readdir返回任何内容。变种最后= gen.next();
//这相当于最终=未定义;
//因为你正在返回的产量的结果,那就是传递的值
//成。接下来(),并且没有任何传递给它。

希望这使得它更清楚什么你还是叫 READDIR 同步,并且没有经过任何回调,因此它可能会引发错误或东西。

那么,如何从发电机得到很好的行为?

通常,这是通过使发电机产生的重新presents READDIR 的结果实际上已计算出的值前一个特殊的对象来完成的。

有关(不现实)例如,收益 ING功能是一种简单的方法来产生了重新presents的有价值的东西。

 功能*读取(路径){
    ,回报率函数(回调){
        fs.readdir(路径,回调);
    };
}VAR根=读(路径);
//根现在是一个生成器对象。变种第一= gen.next();
//这相当于第一=函数(回调){...};//触发回调这里计算的值。
第一个(函数(ERR,DIR){
  变种dirData = gen.next(DIR);
  //这将直接返回'目录',因为我们是直接返回产生价值。  //为所欲为。
});

说真的,你想这种类型的逻辑继续调用发电机,直到所有的收益率呼叫完成,而不是硬编码每次调用。虽然与本通知的主要事情,现在是发电机本身看起来同步,和一切函数外是超级通用的。

您需要某种发电机外壳的功能处理这个屈服值的过程,你的 暂停示例 正是这一点。另一个例子是

有关的返回的东西重新presenting值方法的标准方法是返回 承诺 因为像我一样返回一个函数是一种丑陋的。

随着库,你做到以上没有例子功能:

  VAR thunkify =要求('thunkify');
VAR合作=要求(CO);
变种FS =要求(FS);
VAR READDIR = thunkify(fs.readdir);合作(功能*(){
    //`readdir`将调用节点的功能,并返回一个thunk重新presenting的
    //目录,然后`yield`ed到`co`,这将等待数据
    //要准备好,然后它会再次启动发电机,传递值
    //作为`yield`的结果。
    VAR dirData =产量READDIR(路径,回调);    //为所欲为。
})(功能(错了,结果){
    //一旦同步前瞻性发电机已恢复此回调被调用。
    //或抛出的异常。
});

更新

您更新还是有一些混乱。如果你希望你的列表函数是一台发电机,那么你就需要使用之外列表EM> 无论你在呼唤它。 有限公司内的一切应该是发电机为基础,外面的一切应该是回调为主。 不作列表自动同步。 用于转换基于发电机异步流量控制为基于回调的流量控制。

例如

 列表:功能(路径,回调){
    合作(功能*(){
      VAR列表=产量READDIR(路径);      //使用`list`就在这里。      返回列表;
    })(功能(错了,结果){
      如果你的'READDIR呼叫有一个错误//这里犯错会被设置
      //结果是'共同'的返回值,因此这将是'名单。      回调(ERR,结果);
    })
}

I've been very excited about Node JS for awhile. I finally decided to knuckle down and write a test project to learn about generators in the latest Harmony build of Node.

Here is my very simple test project:

https://github.com/kirkouimet/project-node

To run my test project, you can easily pull the files from Github and then run it with:

node --harmony App.js

Here's my problem - I can't seem to get Node's asynchronous fs.readdir method to run inline with generators. Other projects out there, such as Galaxy and suspend seem to be able to do it.

Here is the block of code I need to fix. I want to be able to instantiate an object of type FileSystem and call the .list() method on it:

https://github.com/kirkouimet/project-node/blob/4c77294f42da9e078775bb84c763d4c60f21e1cc/FileSystem.js#L7-L11

FileSystem = Class.extend({

    construct: function() {
        this.currentDirectory = null;
    },

    list: function*(path) {
        var list = yield NodeFileSystem.readdir(path);

        return list;
    }

});

Do I need to do something ahead of time to convert Node's fs.readdir into a generator?

One important note, I am parsing all class functions as they are created. This lets me handle generator functions differently than normal functions:

https://github.com/kirkouimet/project-node/blob/4c77294f42da9e078775bb84c763d4c60f21e1cc/Class.js#L31-L51

I've been really stumped with this project. Would love any assistance!

Here is what I am trying to accomplish:

  1. Heavy use of classes with a modified version of John Resig's JavaScript Class support with inheritance
  2. Using generators to get inline support for Node's stock async calls

Edit

I've tried to implement your example function and I am running into some trouble.

list: function*(path) {
    var list = null;

    var whatDoesCoReturn = co(function*() {
        list = yield readdir(path);
        console.log(list); // This shows an array of files (good!)
        return list; // Just my guess that co should get this back, it doesn't
    })();
    console.log(whatDoesCoReturn); // This returns undefined (sad times)

    // I need to use `list` right here

    return list; // This returns as null
}

解决方案

First and foremost, it is important to have a good model in your head of exactly what a generator is. A generator function is a function that returns a generator object, and that generator object will step through yield statements within the generator function as you call .next() on it.

Given that description, you should notice that asynchronous behavior is not mentioned. Any action on a generator on its own is synchronous. You can run to the first yield immediately and then do a setTimeout and then call .next() to go to the next yield, but it is the setTimeout that causes asynchronous behavior, not the generator itself.

So let's cast this in the light of fs.readdir. fs.readdir is an async function, and using it in a generator on its own will have no effect. Let's look at your example:

function * read(path){
    return yield fs.readdir(path);
}

var gen = read(path);
// gen is now a generator object.

var first = gen.next();
// This is equivalent to first = fs.readdir(path);
// Which means first === undefined since fs.readdir returns nothing.

var final = gen.next();
// This is equivalent to final = undefined;
// Because you are returning the result of 'yield', and that is the value passed
// into .next(), and you are not passing anything to it.

Hopefully it makes it clearer that what you are still calling readdir synchronously, and you are not passing any callback, so it will probably throw an error or something.

So how do you get nice behavior from generators?

Generally this is accomplished by having the generator yield a special object that represents the result of readdir before the value has actually been calculated.

For (unrealistic) example, yielding a function is a simple way to yield something that represents the value.

function * read(path){
    return yield function(callback){
        fs.readdir(path, callback);
    };
}

var gen = read(path);
// gen is now a generator object.

var first = gen.next();
// This is equivalent to first = function(callback){ ... };

// Trigger the callback to calculate the value here.
first(function(err, dir){
  var dirData = gen.next(dir);
  // This will just return 'dir' since we are directly returning the yielded value.

  // Do whatever.
});

Really, you would want this type of logic to continue calling the generator until all of the yield calls are done, rather than hard-coding each call. The main thing to notice with this though, is now the generator itself looks synchronous, and everything outside the read function is super generic.

You need some kind of generator wrapper function that handles this yield value process, and your example of the suspend does exactly this. Another example is co.

The standard method for the method of "return something representing the value" is to return a promise or a thunk since returning a function like I did is kind of ugly.

With the thunk and co libraries, you with do the above without the example function:

var thunkify = require('thunkify');
var co = require('co');
var fs = require('fs');
var readdir = thunkify(fs.readdir);

co(function * (){
    // `readdir` will call the node function, and return a thunk representing the
    // directory, which is then `yield`ed to `co`, which will wait for the data
    // to be ready, and then it will start the generator again, passing the value
    // as the result of the `yield`.
    var dirData = yield readdir(path, callback);

    // Do whatever.
})(function(err, result){
    // This callback is called once the synchronous-looking generator has returned.
    // or thrown an exception.
});

Update

Your update still has some confusion. If you want your list function to be a generator, then you will need to use co outside of list wherever you are calling it. Everything inside of co should be generator-based and everything outside co should be callback-based. co does not make list automatically asynchronous. co is used to translate a generator-based async flow control into callback-based flow control.

e.g.

list: function(path, callback){
    co(function * (){
      var list = yield readdir(path);

      // Use `list` right here.

      return list;
    })(function(err, result){
      // err here would be set if your 'readdir' call had an error
      // result is the return value from 'co', so it would be 'list'.

      callback(err, result);
    })
}

这篇关于了解节点JS发电机以fs模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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