139

関数を公開するライブラリを維持しているとしますgetData。ユーザーは実際のデータを取得するためにそれを呼び出します:
var output = getData();
内部データはファイルに保存されるため、getDataNode.js 組み込み を使用して実装しますfs.readFileSync。どちらも明らかでgetDataありfs.readFileSync、同期機能です。ある日、基になるデータ ソースを、非同期でのみアクセスできる MongoDB などのリポジトリに切り替えるように言われました。また、ユーザーを怒らせないようにと言われましたgetData。API を変更して、単に promise を返したり、コールバック パラメーターを要求したりすることはできません。どうすれば両方の要件を満たすことができますか?

callback/promise を使用した非同期関数は、JavasSript と Node.js の DNA です。重要な JS アプリには、おそらくこのコーディング スタイルが浸透しています。しかし、この慣行は、いわゆるコールバック ピラミッド オブ ドゥームに簡単につながる可能性があります。さらに悪いことに、呼び出しチェーンのいずれかの呼び出し元のコードが非同期関数の結果に依存している場合、それらのコードもコールバック関数でラップする必要があり、呼び出し元にコーディング スタイルの制約が課せられます。大規模なグローバル リファクタリングを回避するために、非同期関数 (多くの場合、サード パーティのライブラリで提供される) を同期関数にカプセル化する必要があることに時々気付きます。この件に関する解決策を探すと、通常はノード ファイバーに行き着きます。またはそれから派生した npm パッケージ。しかし、ファイバーは私が直面している問題を解決することはできません. ファイバーズの著者が提供した例でさえ、欠陥を示しています。

...
Fiber(function() {
    console.log('wait... ' + new Date);
    sleep(1000);
    console.log('ok... ' + new Date);
}).run();
console.log('back in main');

実際の出力:

wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
back in main
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)

関数ファイバーが実際に非同期関数スリープを同期に変える場合、出力は次のようになります。

wait... Fri Jan 21 2011 22:42:04 GMT+0900 (JST)
ok... Fri Jan 21 2011 22:42:05 GMT+0900 (JST)
back in main

JSFiddleで別の簡単な例を作成し、期待される出力を生成するコードを探しています。Node.js でのみ機能するソリューションを受け入れるので、JSFiddle で機能していなくても、任意の npm パッケージを自由に要求できます。

4

10 に答える 10

4

関数ファイバーが本当に非同期関数スリープを同期に変える場合

はい。ファイバー内では、関数はログを記録する前に待機しますok。ファイバーは非同期関数を同期させませんが、非同期関数を使用する同期のように見えるコードを記述し、Fiber.

大規模なグローバル リファクタリングを回避するために、非同期関数を同期関数にカプセル化する必要があることに時々気付きます。

それはいけません。非同期コードを同期にすることはできません。グローバル コードでそれを予測し、最初から非同期スタイルで記述する必要があります。グローバル コードをファイバーでラップするか、promise を使用するか、promise ジェネレーターを使用するか、単純なコールバックを使用するかは、好みによって異なります。

私の目的は、データ取得方法が同期から非同期に変更されたときの呼び出し元への影響を最小限に抑えることです

プロミスとファイバーの両方がそれを行うことができます。

于 2014-02-17T19:07:31.137 に答える
2

現在、このジェネレーター パターンは、多くの状況での解決策となります。

以下は、async readline.question 関数を使用した nodejs の順次コンソール プロンプトの例です。

var main = (function* () {

  // just import and initialize 'readline' in nodejs
  var r = require('readline')
  var rl = r.createInterface({input: process.stdin, output: process.stdout })

  // magic here, the callback is the iterator.next
  var answerA = yield rl.question('do you want this? ', r=>main.next(r))    

  // and again, in a sync fashion
  var answerB = yield rl.question('are you sure? ', r=>main.next(r))        

  // readline boilerplate
  rl.close()

  console.log(answerA, answerB)

})()  // <-- executed: iterator created from generator
main.next()     // kick off the iterator, 
                // runs until the first 'yield', including rightmost code
                // and waits until another main.next() happens
于 2016-08-17T20:36:13.560 に答える
1

ノードファイバーを使用して解決できないシナリオが見つかりません。node-fibers を使用して提供した例は、期待どおりに動作します。重要なのは、関連するすべてのコードをファイバー内で実行することです。これにより、ランダムな位置で新しいファイバーを開始する必要がなくなります。

例を見てみましょう:アプリケーションのエントリ ポイントであるフレームワークを使用しているとします (このフレームワークは変更できません)。このフレームワークは、nodejs モジュールをプラグインとしてロードし、プラグインでいくつかのメソッドを呼び出します。このフレームワークは同期関数のみを受け入れ、それ自体ではファイバーを使用しないとしましょう。

プラグインの 1 つで使用したいライブラリがありますが、このライブラリは非同期であり、変更したくありません。

ファイバーが実行されていない場合、メイン スレッドを生成することはできませんが、ファイバーを使用してプラグインを作成することはできます。ファイバー内でフレームワーク全体を開始するラッパー エントリを作成するだけで、プラグインから実行を引き渡すことができます。

欠点: フレームワークがsetTimeoutorPromiseを内部で使用している場合、ファイバー コンテキストをエスケープします。これは、、、およびすべてのイベント ハンドラをモックすることで回避できsetTimeoutますPromise.then

Promiseしたがって、これが aが解決されるまでファイバーを生成する方法です。このコードは async (Promise を返す) 関数を使用し、Promise が解決されるとファイバーを再開します。

フレームワーク-entry.js

console.log(require("./my-plugin").run());

async-lib.js

exports.getValueAsync = () => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("Async Value");
    }, 100);
  });
};

my-plugin.js

const Fiber = require("fibers");

function fiberWaitFor(promiseOrValue) {
  var fiber = Fiber.current, error, value;
  Promise.resolve(promiseOrValue).then(v => {
    error = false;
    value = v;
    fiber.run();
  }, e => {
    error = true;
    value = e;
    fiber.run();
  });
  Fiber.yield();
  if (error) {
    throw value;
  } else {
    return value;
  }
}

const asyncLib = require("./async-lib");

exports.run = () => {
  return fiberWaitFor(asyncLib.getValueAsync());
};

my-entry.js

require("fibers")(() => {
  require("./framework-entry");
}).run();

実行node framework-entry.jsすると、エラーがスローされます: Error: yield() called with no fiber running. 実行するnode my-entry.jsと、期待どおりに動作します。

于 2016-07-14T14:26:15.880 に答える
-1

Node.js コードを同期させることは、データベースなどのいくつかの側面で不可欠です。しかし、Node.js の実際の利点は非同期コードにあります。シングルスレッドノンブロッキングなので。

重要な機能 Fiber() を使用して同期できます。 await() と defer() を使用します。次に、コールバック関数を defer() に置き換えます。

通常の非同期コード。これは CallBack 関数を使用します。

function add (var a, var b, function(err,res){
       console.log(res);
});

 function sub (var res2, var b, function(err,res1){
           console.log(res);
    });

 function div (var res2, var b, function(err,res3){
           console.log(res3);
    });

Fiber()、await()、および defer() を使用して上記のコードを同期します。

fiber(function(){
     var obj1 = await(function add(var a, var b,defer()));
     var obj2 = await(function sub(var obj1, var b, defer()));
     var obj3 = await(function sub(var obj2, var b, defer()));

});

これが役立つことを願っています。ありがとうございました

于 2015-07-14T15:51:59.103 に答える
-2

最初は node.js でこれに苦労しましたが、async.js は、これに対処するのに役立つことがわかった最高のライブラリです。ノードで同期コードを書きたい場合は、この方法でアプローチします。

var async = require('async');

console.log('in main');

doABunchOfThings(function() {
  console.log('back in main');
});

function doABunchOfThings(fnCallback) {
  async.series([
    function(callback) {
      console.log('step 1');
      callback();
    },
    function(callback) {
      setTimeout(callback, 1000);
    },
    function(callback) {
      console.log('step 2');
      callback();
    },
    function(callback) {
      setTimeout(callback, 2000);
    },
    function(callback) {
      console.log('step 3');
      callback();
    },
  ], function(err, results) {
    console.log('done with things');
    fnCallback();
  });
}

このプログラムは常に以下を生成します...

in main
step 1
step 2
step 3
done with things
back in main
于 2014-05-01T13:59:31.383 に答える
-12

Javascript はシングル スレッド言語です。サーバー全体をブロックしたくありません。非同期コードは、依存関係を明示的にすることで競合状態を排除します。

非同期コードを愛することを学びましょう!

promisesコールバック地獄のピラミッドを作成せずに非同期コードを確認してください。node.js には promiseQ ライブラリをお勧めします

httpGet(url.parse("http://example.org/")).then(function (res) {
    console.log(res.statusCode);  // maybe 302
    return httpGet(url.parse(res.headers["location"]));
}).then(function (res) {
    console.log(res.statusCode);  // maybe 200
});

http://howtonode.org/promises

編集:これは私の最も物議を醸す答えです。ノードにはyieldキーワードがあり、非同期コードを同期しているかのように扱うことができます。http://blog.alexmaccaw.com/how-yield-will-transform-node

于 2014-02-17T02:50:10.890 に答える