3

我一直在玩 jsdom,它是 node.js 的一个模块。底部的以下代码来自他们的文档页面。我的问题是如何从异步函数返回一些东西。

我知道这是一个经常被问到的问题,可能我也是。我也知道回调是解决这类问题的好朋友。我的目标是找到一种解决方法,它可能类似于 PHP 中的 cookie 或 Session 变量,以便将那一点点数据传输到异步函数之外的外部范围。然后,一旦从外部范围设置数据,它就应该可以访问。

我想知道的第一件事是:

  1. 是否已经有一种方法可以将数据存储在某个地方,例如存在于外部范围内的 cookie 或会话,并且一旦我完成了我必须做的事情就可以访问?
  2. 如果我要在代码中的B点将数据写入文件,并在C点读取它,我是否必须编写某种超时函数来等待几秒钟才能读取文件?我在 nodejs 中使用异步函数的经验有时表明,在尝试读取之前,我必须等待几秒钟才能完成写入过程。这里也会出现这种情况吗?如果是,那是否意味着如果我保存数据的位置是内存,就必须发生这种情况?
  3. 如果我要为此目的编写一个 c++ 插件,它充当一个单独的数据托架,我们可以save(data)B点到内存,retrieve(data)C点从内存;这行得通吗?

老实说,我不喜欢编写临时文件来解决异步函数。我一直在寻找一种简单而有效的方法来传递数据,但我需要像您这样有经验的程序员的一些指导,以超越解决这个问题的不必要的方法。

如果您能为我提出一些想法,说明什么可行,什么不可行,我将不胜感激。

这是示例代码:

// Print all of the news items on hackernews
var jsdom = require("jsdom");
// var result; 
// A) Outer Scope: Since the function is async in done, storing the result here and echoing in point C is pointless.
jsdom.env({
  html: "http://news.ycombinator.com/",
  scripts: ["http://code.jquery.com/jquery.js"],
  done: function (errors, window) {
    var $ = window.$;
    console.log("HN Links");
    $("td.title:not(:last) a").each(function() {
      console.log(" -", $(this).text());
    });
    // B) let's say I want to return something I've scavenged here.
    // result = $("a");
  }
});
// C) 
// console.log(result)
4

2 回答 2

7

您需要清除您认为文件中较低代码发生在稍后时间的同步体验。它不一定在节点中这样做。这是交易。在节点中,您像在餐厅一样下订单,而不是像这样:

1. Order a salad
2. Wait 11 minutes
3. Eat the salad

你这样做

1. Order a salad
2. Wait for the server to serve you the salad
3. Eat the salad

第一个例子是你的程序中的一个竞争条件和一个可怕的错误,它会导致沙拉无缘无故地等待被吃掉,或者试图吃一个还没有的沙拉。

不要认为“我想在这里返回一些东西”,而认为“这个数据已经准备好了”。所以你可以拥有:

function eatSalad() {...}
placeOrder("salad", eatSalad);

eatSalad例程的回调在哪里placeOrder,它执行必要的 I/O 来获取沙拉。请注意,即使eatSalad在文件中较早,它也会按时间顺序发生在后面。你不返回任何东西,你用你准备好的数据调用回调。

这是您的异步​​代码段。

// Print all of the news items on hackernews
var jsdom = require("jsdom");
// var result; 
// A) Outer Scope: Since the function is async in done, storing the result here and echoing in point C is pointless.
jsdom.env({
  html: "http://news.ycombinator.com/",
  scripts: ["http://code.jquery.com/jquery.js"],
  done: function (errors, window) {
    var $ = window.$;
    console.log("HN Links");
    $("td.title:not(:last) a").each(function() {
      console.log(" -", $(this).text());
    });
    // B) let's say I want to return something I've scavenged here.
    // result = $("a");
    resultIsReady($("a"));
  }
});

function resultIsReady(element) {
    console.log(element);
}

编辑添加以从评论中回答您的问题,节点代码通常不是由返回事物的函数构建的,而是由调用带有“返回值”的回调函数的函数构建的。该return关键字仅用于实际返回不执行任何 I/O 的内存中代码的值。所以求内存数组的均值可以直接返回,但从数据库结果集中求均值必须调用回调函数。基本范式是从这样的函数(伪代码 DB 库)构建您的程序:

function getUser(email, callback) {
    db.users.where({email: email}, function (error, user) {
        if (error) {
            //This return statement is just for early termination
            //of the function. The value returned is discarded
            return callback(error);
        }
        callback(null, user);
    });;
}

所以这就是你做事的方式。并且通常像这样的函数会执行非常有限的 IO 调用(1 或 2 次很常见,然后您开始陷入嵌套地狱并需要重构)。

我个人编写了很多这样的函数,然后使用异步库来描述需要发生的事情的高阶序列。还有很多其他流行的流控制库,有些人喜欢 Promise 模式。然而,目前一些核心节点社区成员似乎提倡将回调作为一种真正的方式,而承诺似乎已经失宠。

于 2013-05-26T07:44:39.740 回答
3

避免在任何可能阻塞执行的地方使用同步代码,例如数据库操作、文件 IO、网络数据检索、长计算等。在您的示例中,当您完成计算以继续执行时使用回调,您可以还可以查看 async https://npmjs.org/package/async库,它是毛发调用的事实上的标准:

    function sendData(result) {
        res.json(result);
    }

    var jsdom = require("jsdom");
    // var result;
    // A) Outer Scope: Since the function is async in done, storing the result here 
    // and echoing in point C is pointless.
    jsdom.env({
        html: "http://news.ycombinator.com/",
        scripts: ["http://code.jquery.com/jquery.js"],
        done: function (errors, window) {
            var $ = window.$;
            console.log("HN Links");
            $("td.title:not(:last) a").each(function () {
                console.log(" -", $(this).text());
            });
            // B) let's say I want to return something I've scavenged here.
            var result = $("a");
            sendData(result);
        }
    });
于 2013-05-26T07:57:44.507 回答