নোড.জেএসসে অ্যাসিক্রোনাস ফাংশনগুলির দীর্ঘ বাসা কীভাবে এড়ানো যায়


158

আমি একটি পৃষ্ঠা তৈরি করতে চাই যা কোনও ডিবি থেকে কিছু ডেটা প্রদর্শন করে, তাই আমি এমন কিছু ফাংশন তৈরি করেছি যা আমার ডিবি থেকে সেই ডেটা পায়। আমি নোড.জেএস-এ কেবল নবাগত, যতদূর আমি বুঝতে পেরেছি, যদি আমি তাদের সমস্তগুলি একটি পৃষ্ঠায় ব্যবহার করতে চাই (এইচটিটিপি প্রতিক্রিয়া) তবে আমি তাদের সবকটি বাসা বাঁধতে হবে:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  getSomeDate(client, function(someData) {
    html += "<p>"+ someData +"</p>";
    getSomeOtherDate(client, function(someOtherData) {
      html += "<p>"+ someOtherData +"</p>";
      getMoreData(client, function(moreData) {
        html += "<p>"+ moreData +"</p>";
        res.write(html);
        res.end();
      });
    });
  });

যদি এর মতো অনেকগুলি ক্রিয়া থাকে তবে বাসা বাঁধতে সমস্যা হয়ে যায়

এই সমস্যা এড়ানোর জন্য একটি উপায় আছে কি? আমার ধারণা এটি কীভাবে আপনি একাধিক অ্যাসিনক্রোনাস ফাংশনগুলিকে একত্রিত করেন যা কিছু মৌলিক বলে মনে হয় with


12
সুতরাং আপনার যখন 10 অ্যাসিঙ্ক ফাংশন রয়েছে, তখন আপনার 10 টি মাত্রার ইনডেন্টেশন রয়েছে?
কে প্যালে

এই লিঙ্কটি সাহায্য করতে পারে। stackoverflow.com/a/4631909/290340
ইভান প্লেস

1
আরেকটি সমস্যা: মধ্যে আরেকটি ফাংশন ঢোকাতে getSomeDateএবং getSomeOtherDateঅনেক লাইনের খাঁজ যা Git ইতিহাস কঠিন করে তোলে পড়তে পরিবর্তন প্রান্ত আপ ( git blameএই পরেও অনর্থক), এবং আপনি সম্ভবত বাগ যখন নিজে এই করছেন
ড্যানিয়েল এল্ডযার

উত্তর:


73

আকর্ষণীয় পর্যবেক্ষণ। নোট করুন যে জাভাস্ক্রিপ্টে আপনি সাধারণত নামযুক্ত ফাংশন ভেরিয়েবলগুলির সাথে ইনলাইন বেনামে কলব্যাক ফাংশন প্রতিস্থাপন করতে পারেন।

পরবর্তী:

http.createServer(function (req, res) {
   // inline callback function ...

   getSomeData(client, function (someData) {
      // another inline callback function ...

      getMoreData(client, function(moreData) {
         // one more inline callback function ...
      });
   });

   // etc ...
});

এরকম কিছু দেখতে আবারও লেখা যেতে পারে:

var moreDataParser = function (moreData) {
   // date parsing logic
};

var someDataParser = function (someData) {
   // some data parsing logic

   getMoreData(client, moreDataParser);
};

var createServerCallback = function (req, res) {
   // create server logic

   getSomeData(client, someDataParser);

   // etc ...
};

http.createServer(createServerCallback);

তবে আপনি যদি অন্য জায়গায় কলব্যাক লজিকের পুনরায় ব্যবহার করার পরিকল্পনা না করেন তবে আপনার উদাহরণ হিসাবে যেমন ইনলাইন বেনামে ফাংশনগুলি পড়া প্রায়শই সহজ। এটি সমস্ত কলব্যাকের জন্য একটি নাম খুঁজে পাওয়া থেকেও আপনাকে বাঁচায়।

অতিরিক্ত নোট করুন যে যেমন @ পিএসটি নীচে একটি মন্তব্যে উল্লেখ করেছেন, আপনি যদি অভ্যন্তরীণ ফাংশনগুলির মধ্যে ক্লোজার ভেরিয়েবলগুলি অ্যাক্সেস করে থাকেন তবে উপরেরটি একটি সরল অনুবাদ হবে না। এই ধরনের ক্ষেত্রে, ইনলাইন বেনামে ফাংশনগুলি ব্যবহার করা আরও বেশি পছন্দনীয়।


26
যাইহোক, (এবং সত্যিই কেবল বাণিজ্য বন্ধ বোঝার জন্য) যখন অহংসাবস্থায় পরিবর্তনশীল উপর কিছু ক্লোজার শব্দার্থবিজ্ঞান হারিয়ে যেতে পারে তাই এটি সরাসরি অনুবাদ নয়। উপরের উদাহরণে 'রেজ' ইন অ্যাক্সেস getMoreDataহারিয়েছে।

2
আমি মনে করি আপনার সমাধানটি নষ্ট হয়েছে: someDataParserআসলে সমস্ত ডেটা পার্স করে, কারণ এটি কল করে getMoreData। সেই অর্থে, ফাংশনটির নামটি ভুল এবং এটি স্পষ্ট হয়ে যায় যে আমরা আসলে বাসা বাঁধার সমস্যাটি সরিয়ে নেই।
কনস্টান্টিন শুবার্ট

63

কে, খালি এই মডিউলগুলির একটি ব্যবহার করুন।

এটি এটি ঘুরিয়ে দেবে:

dbGet('userIdOf:bobvance', function(userId) {
    dbSet('user:' + userId + ':email', 'bobvance@potato.egg', function() {
        dbSet('user:' + userId + ':firstName', 'Bob', function() {
            dbSet('user:' + userId + ':lastName', 'Vance', function() {
                okWeAreDone();
            });
        });
    });
});

এটিতে:

flow.exec(
    function() {
        dbGet('userIdOf:bobvance', this);

    },function(userId) {
        dbSet('user:' + userId + ':email', 'bobvance@potato.egg', this.MULTI());
        dbSet('user:' + userId + ':firstName', 'Bob', this.MULTI());
        dbSet('user:' + userId + ':lastName', 'Vance', this.MULTI());

    },function() {
        okWeAreDone()
    }
);

9
ফ্লো-জেএস, পদক্ষেপ এবং অ্যাসিঙ্কের একটি তাত্ক্ষণিক নজর রেখেছিল এবং মনে হয় যে তারা কেবল ফাংশন সম্পাদনের ক্রম নিয়ে কাজ করে। আমার ক্ষেত্রে প্রতিটি ইনডেন্টেশনে ইনলাইন ক্লোজার ভেরিয়েবলের অ্যাক্সেস রয়েছে। উদাহরণস্বরূপ, ফাংশনগুলি এর মতো কাজ করে: এইচটিটিপি রেকর্ড / রেজোলিউশন পান, কুকির জন্য ডিবি থেকে ইউজারিড পান, পরবর্তী ব্যবহারকারীটির জন্য ইমেল পান, পরবর্তী ইমেলের জন্য আরও ডেটা পান, ..., পরবর্তী ওয়াইয়ের জন্য এক্স পান, ... যদি আমি ভুল না হয়ে থাকি তবে এই ফ্রেমওয়ার্কগুলি কেবল আশ্বাস দেয় যে অ্যাসিঙ্ক ফাংশনগুলি সঠিক ক্রমে কার্যকর করা হবে, তবে প্রতিটি ফাংশন বডিতে ক্লোজারগুলি (?) ধন্যবাদ দ্বারা প্রাকৃতিকভাবে ভেরিয়েবল সরবরাহ করার উপায় নেই :) ধন্যবাদ
কে প্যালে

9
এই লাইব্রেরি র‌্যাঙ্কিংয়ের ক্ষেত্রে আমি গিথুবে প্রত্যেকের উপরে "তারা" সংখ্যাটি পরীক্ষা করেছি। অ্যাসিঙ্কের প্রায় 3000 নিয়ে সর্বাধিক রয়েছে, ধাপটি 1000 এর পরে এবং অন্যগুলি উল্লেখযোগ্যভাবে কম। অবশ্যই, তারা সকলেই একই জিনিসটি করে না :-)
কিলজিলিন

3
@ কেপ্যালে আমি অ্যাসিঙ্ক.ওয়াটারফ্রোল ব্যবহার করার প্রবণতা রাখি এবং প্রতিটি পর্যায়ে / পদক্ষেপের জন্য আমার নিজস্ব ফাংশন থাকবে যা পরবর্তী পদক্ষেপের প্রয়োজন অনুসারে চলে যাবে বা অ্যাসিঙ্ক M মেথডএড কলের আগে ভেরিয়েবলগুলি সংজ্ঞায়িত করবে যাতে এটি ডাউনলাইন উপলব্ধ থাকে। এছাড়াও আমার অ্যাসিঙ্ক। * কলগুলির জন্য METHODNAME.bind (...) ব্যবহার করবে, যা খুব সুন্দরভাবে কার্যকর হয়।
ট্র্যাকার 1

একটি তাত্ক্ষণিক প্রশ্ন: আপনার মডিউলগুলির তালিকায়, শেষ দুটি কি একই? অর্থাৎ "async.js" এবং "async"
dari0h

18

বেশিরভাগ ক্ষেত্রে, আমি ড্যানিয়েল ভ্যাসালোর সাথে একমত হই। আপনি যদি কোনও জটিল এবং গভীরভাবে নেস্টেড ফাংশনটিকে পৃথক নামকরণের ফাংশনগুলিতে ভাঙতে পারেন, তবে এটি সাধারণত একটি ভাল ধারণা। এটি যখন কোনও একক ক্রিয়াকলাপের মধ্যে করার জন্য বোধগম্য হয় তখন আপনি অনেকগুলি নোড.জেএসএস অ্যাসিঙ্ক লাইব্রেরি ব্যবহার করতে পারেন। লোকেদের এটিকে মোকাবেলা করার জন্য বিভিন্ন উপায় নিয়ে এসেছে, সুতরাং নোড.জেএস মডিউল পৃষ্ঠাগুলি একবার দেখুন এবং আপনার মতামতটি দেখুন।

আমি এটির জন্য একটি মডিউল লিখেছি, যাকে async.js বলা হয় । এটি ব্যবহার করে উপরের উদাহরণটি আপডেট করা যেতে পারে:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  async.series({
    someData: async.apply(getSomeDate, client),
    someOtherData: async.apply(getSomeOtherDate, client),
    moreData: async.apply(getMoreData, client)
  },
  function (err, results) {
    var html = "<h1>Demo page</h1>";
    html += "<p>" + results.someData + "</p>";
    html += "<p>" + results.someOtherData + "</p>";
    html += "<p>" + results.moreData + "</p>";
    res.write(html);
    res.end();
  });
});

এই পদ্ধতির সম্পর্কে একটি দুর্দান্ত বিষয় হ'ল আপনি 'সিরিজ' ফাংশনটিকে 'সমান্তরাল' করে পরিবর্তন করে সমান্তরালে ডেটা আনার জন্য আপনার কোডটি দ্রুত পরিবর্তন করতে পারেন। আরও কী, async.js ব্রাউজারের ভিতরেও কাজ করবে, যাতে আপনি নোড.জেজে যেমন পদ্ধতি ব্যবহার করতে পারেন তখন আপনার কোনও জটিল অ্যাসিঙ্ক কোডের মুখোমুখি হওয়া উচিত।

আশা করি এটি কার্যকর!


হাই ক্যালান এবং উত্তরের জন্য ধন্যবাদ! আমার ক্ষেত্রে প্রতিটি ইনডেন্টেশনে ইনলাইন ক্লোজার ভেরিয়েবলের অ্যাক্সেস রয়েছে। উদাহরণস্বরূপ, ফাংশনগুলি এর মতো কাজ করে: এইচটিটিপি রেকর্ড / রেজোলিউশন পান, কুকির জন্য ডিবি থেকে ইউজারিড পান, পরবর্তী ব্যবহারকারীটির জন্য ইমেল পান, পরবর্তী ইমেলের জন্য আরও ডেটা পান, ..., পরবর্তী ওয়াইয়ের জন্য এক্স পান, ... যদি আমি ভুল না হয়ে থাকি তবে আপনার প্রস্তাবিত কোডটি কেবলমাত্র আশ্বাস দেয় যে অ্যাসিঙ্ক ফাংশনগুলি সঠিক ক্রমে কার্যকর করা হবে, তবে প্রতিটি ফাংশন বডিটিতে আমার মূল কোডটিতে ক্লোজারগুলি দ্বারা প্রাকৃতিকভাবে ভেরিয়েবল সরবরাহ করার উপায় নেই। এটাই কি?
কে প্যেল

3
আপনি যা অর্জন করতে চাইছেন তাকে আর্কিটেকচারালি ডেটা পাইপলাইন বলা হয়। আপনি এই ধরনের ক্ষেত্রে অ্যাসিঙ্ক জলপ্রপাতটি ব্যবহার করতে পারেন।
রুডলফ মেইজিং

18

আপনি এই কৌশলটি নেস্টেড ফাংশন বা মডিউলের চেয়ে অ্যারে দিয়ে ব্যবহার করতে পারেন।

চোখে অনেক সহজ।

var fs = require("fs");
var chain = [
    function() { 
        console.log("step1");
        fs.stat("f1.js",chain.shift());
    },
    function(err, stats) {
        console.log("step2");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step3");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step4");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("step5");
        fs.stat("f2.js",chain.shift());
    },
    function(err, stats) {
        console.log("done");
    },
];
chain.shift()();

সমান্তরাল প্রক্রিয়া বা এমনকি প্রক্রিয়াগুলির সমান্তরাল চেইনের জন্য আপনি প্রতিমাটি প্রসারিত করতে পারেন:

var fs = require("fs");
var fork1 = 2, fork2 = 2, chain = [
    function() { 
        console.log("step1");
        fs.stat("f1.js",chain.shift());
    },
    function(err, stats) {
        console.log("step2");
        var next = chain.shift();
        fs.stat("f2a.js",next);
        fs.stat("f2b.js",next);
    },
    function(err, stats) {
        if ( --fork1 )
            return;
        console.log("step3");
        var next = chain.shift();

        var chain1 = [
            function() { 
                console.log("step4aa");
                fs.stat("f1.js",chain1.shift());
            },
            function(err, stats) { 
                console.log("step4ab");
                fs.stat("f1ab.js",next);
            },
        ];
        chain1.shift()();

        var chain2 = [
            function() { 
                console.log("step4ba");
                fs.stat("f1.js",chain2.shift());
            },
            function(err, stats) { 
                console.log("step4bb");
                fs.stat("f1ab.js",next);
            },
        ];
        chain2.shift()();
    },
    function(err, stats) {
        if ( --fork2 )
            return;
        console.log("done");
    },
];
chain.shift()();

15

আমি এই উদ্দেশ্যে async.js অনেক পছন্দ করি

জলপ্রপাত কমান্ড দ্বারা সমস্যাটি সমাধান করা হয়েছে:

জলপ্রপাত (কাজগুলি, [কলব্যাক])

সিরিজে ক্রিয়াকলাপগুলির একটি অ্যারে চালায়, প্রত্যেকে তাদের ফলাফলগুলি অ্যারেতে পরের দিকে পাস করে। যাইহোক, যদি ফাংশনগুলির কোনও কলব্যাকটিতে ত্রুটিটি পাস করে, তবে পরবর্তী ক্রিয়াকলাপটি কার্যকর হয় না এবং ত্রুটি সহ তাত্ক্ষণিকভাবে মূল কলব্যাক বলা হয়।

যুক্তি

টাস্ক - চালানোর জন্য একটি ক্রিয়াকলাপ, প্রতিটি ফাংশন একটি কলব্যাক পাস হয় (ত্রুটি, ফলাফল 1, ফলাফল 2, ...) এটি সম্পূর্ণ হওয়ার পরে কল করতে হবে। প্রথম আর্গুমেন্টটি একটি ত্রুটি (যা নাল হতে পারে) এবং পরবর্তী কোনও কাজের জন্য আর্গুমেন্ট হিসাবে পরবর্তী কোনও আর্গুমেন্ট পাস করা হবে। কলব্যাক (এরর, [ফলাফল]) - সমস্ত ফাংশন শেষ হয়ে গেলে চালানোর জন্য একটি optionচ্ছিক কলব্যাক। এটি শেষ টাস্কের কলব্যাকের ফলাফলটি পাস করবে।

উদাহরণ

async.waterfall([
    function(callback){
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback){
        callback(null, 'three');
    },
    function(arg1, callback){
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
    // result now equals 'done'    
});

রেক, রেস ভেরিয়েবলগুলি হিসাবে, সেগুলি একই ফাংশন (req, res) - এর মধ্যে ভাগ করা হবে}} যা পুরো async. ওয়াটারফল কল কলটি বন্ধ করে দেয়।

শুধু তাই নয়, অ্যাসিঙ্ক খুব পরিষ্কার। আমার অর্থ হ'ল আমি এরকম অনেকগুলি কেস পরিবর্তন করি:

function(o,cb){
    function2(o,function(err, resp){
        cb(err,resp);
    })
}

প্রথমত:

function(o,cb){
    function2(o,cb);
}

তারপরে এটি:

function2(o,cb);

তারপরে এটি:

async.waterfall([function2,function3,function4],optionalcb)

এটি async এর জন্য প্রস্তুত অনেকগুলি প্রাক তৈরির ফাংশনগুলিকে খুব দ্রুত ইউজার.জেএস থেকে কল করার অনুমতি দেয়। আপনি যা করতে চান তা কেবল চেইন করুন, নিশ্চিত করুন ও, সিবি সর্বজনীনভাবে পরিচালিত হয়েছে। এটি পুরো কোডিং প্রক্রিয়াটিকে অনেক গতি দেয়।


11

আপনার যা দরকার তা হল সামান্য সিনট্যাকটিক চিনি। এটি খুঁজে বের করুন:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = ["<h1>Demo page</h1>"];
  var pushHTML = html.push.bind(html);

  Queue.push( getSomeData.partial(client, pushHTML) );
  Queue.push( getSomeOtherData.partial(client, pushHTML) );
  Queue.push( getMoreData.partial(client, pushHTML) );
  Queue.push( function() {
    res.write(html.join(''));
    res.end();
  });
  Queue.execute();
}); 

বেশ সুন্দর , তাই না? আপনি লক্ষ্য করতে পারেন যে এইচটিএমএল একটি অ্যারে হয়ে গেছে। এটি আংশিক কারণ স্ট্রিংগুলি অপরিবর্তনীয়, তাই আপনার আউটপুট একটি অ্যারেতে বাফারিংয়ের চেয়ে আরও বড় এবং বৃহত্তর স্ট্রিংগুলি বাদ দেওয়ার চেয়ে ভাল। অন্য কারণটি হল এর সাথে আরও একটি দুর্দান্ত সিনট্যাক্স bind

Queueউদাহরণে সত্যিই কেবল একটি উদাহরণ এবং পাশাপাশি বরাবর নীচে partialপ্রয়োগ করা যেতে পারে

// Functional programming for the rescue
Function.prototype.partial = function() {
  var fun = this,
      preArgs = Array.prototype.slice.call(arguments);
  return function() {
    fun.apply(null, preArgs.concat.apply(preArgs, arguments));
  };
};

Queue = [];
Queue.execute = function () {
  if (Queue.length) {
    Queue.shift()(Queue.execute);
  }
};

1
কুই.একসিকিউট () সহজেই একের পর এক পার্টিয়ালগুলি কার্যকর করে, অ্যাসিঙ্ক কলগুলির ফলাফলগুলির জন্য অপেক্ষা না করে।
এনজিএন

ধন্যবাদ, ধন্যবাদ আমি উত্তর আপডেট করেছি। এখানে একটি পরীক্ষা দেওয়া হয়েছে: jsbin.com/ebobo5/edit ( optionচ্ছিকlast ফাংশন সহ)
gblazex

হাই গালাম্বলজ এবং উত্তরের জন্য ধন্যবাদ! আমার ক্ষেত্রে প্রতিটি ইনডেন্টেশনে ইনলাইন ক্লোজার ভেরিয়েবলের অ্যাক্সেস রয়েছে। উদাহরণস্বরূপ, ফাংশনগুলি এর মতো কাজ করে: এইচটিটিপি রেকর্ড / রেজোলিউশন পান, কুকির জন্য ডিবি থেকে ইউজারিড পান, পরবর্তী ব্যবহারকারীটির জন্য ইমেল পান, পরবর্তী ইমেলের জন্য আরও ডেটা পান, ..., পরবর্তী ওয়াইয়ের জন্য এক্স পান, ... যদি আমি ভুল না হয়ে থাকি তবে আপনার প্রস্তাবিত কোডটি কেবলমাত্র আশ্বাস দেয় যে অ্যাসিঙ্ক ফাংশনগুলি সঠিক ক্রমে কার্যকর করা হবে, তবে প্রতিটি ফাংশন বডিটিতে আমার মূল কোডটিতে ক্লোজারগুলি দ্বারা প্রাকৃতিকভাবে ভেরিয়েবল সরবরাহ করার উপায় নেই। এটাই কি?
কে প্যালে

1
ঠিক আছে আপনি সমস্ত উত্তরে সংজ্ঞাটি হারাবেন। আপনি যা করতে পারেন তা হ'ল ভাগ করা ডেটার জন্য বিশ্বব্যাপী সুযোগে একটি অবজেক্ট তৈরি করা । সুতরাং উদাহরণস্বরূপ আপনার প্রথম ফাংশনটি যুক্ত হয় obj.emailএবং আপনার পরবর্তী ফাংশনটি obj.emailএটি মুছে ফেলবে (বা কেবল নির্ধারিত করে null)।
gblazex

7

আমি যখন থেকে এটি পেয়েছি তখন থেকেই অ্যাসিঙ্ক.জেএস প্রেমে আছি async.seriesদীর্ঘ বাসা এড়ানোর জন্য এটির একটি ক্রিয়া রয়েছে যা আপনি ব্যবহার করতে পারেন।

নথিপত্র: -


সিরিজ (কাজগুলি, [কলব্যাক])

সিরিজের ক্রিয়াকলাপগুলির একটি অ্যারে চালান, প্রতিটি পূর্ববর্তী ফাংশনটি শেষ হয়ে গেলে প্রতিটি চলমান। [...]

যুক্তি

tasks- সঞ্চালনের জন্য ফাংশনগুলির একটি অ্যারে, প্রতিটি ফাংশন একটি কলব্যাক পাস হয় যা এটি সম্পূর্ণ হওয়ার পরে কল করতে হবে। callback(err, [results])- সমস্ত ফাংশন শেষ হয়ে গেলে চালানোর জন্য একটি alচ্ছিক কলব্যাক। এই ফাংশনটি অ্যারেতে ব্যবহৃত কলব্যাকগুলিতে সমস্ত আর্গুমেন্টের একটি অ্যারে পায় gets


আপনার উদাহরণ কোডে আমরা এটি কীভাবে প্রয়োগ করতে পারি তা এখানে:

http.createServer(function (req, res) {

    res.writeHead(200, {'Content-Type': 'text/html'});

    var html = "<h1>Demo page</h1>";

    async.series([
        function (callback) {
            getSomeData(client, function (someData) { 
                html += "<p>"+ someData +"</p>";

                callback();
            });
        },

        function (callback) {
            getSomeOtherData(client, function (someOtherData) { 
                html += "<p>"+ someOtherData +"</p>";

                callback(); 
            });
        },

        funciton (callback) {
            getMoreData(client, function (moreData) {
                html += "<p>"+ moreData +"</p>";

                callback();
            });
        }
    ], function () {
        res.write(html);
        res.end();
    });
});

6

আমি দেখেছি সবচেয়ে সাধারণ সিনট্যাকটিকাল চিনি হ'ল নোড-প্রতিশ্রুতি।

npm ইনস্টল নোড-প্রতিশ্রুতি || গিট ক্লোন https://github.com/kriszyp/node-promise

এটি ব্যবহার করে আপনি async পদ্ধতিগুলি চেন করতে পারেন:

firstMethod().then(secondMethod).then(thirdMethod);

প্রত্যেকের রিটার্ন মান পরবর্তীটিতে যুক্তি হিসাবে উপলব্ধ।


3

আপনি সেখানে যা করেছেন তা একটি অ্যাসিঞ্চ প্যাটার্ন গ্রহণ করুন এবং এটি যথাযথভাবে ডাকা 3 টি ফাংশনে প্রয়োগ করুন, প্রতিটি প্রত্যেকে প্রারম্ভিক শুরুর আগে শেষ হওয়ার অপেক্ষায় - অর্থাত আপনি এগুলি সিঙ্ক্রোনাস তৈরি করেছেন । অ্যাসিঞ্চ প্রোগ্রামিংয়ের বিষয়টি হ'ল আপনার একসাথে বেশ কয়েকটি ফাংশন চলতে পারে এবং প্রতিটি সম্পূর্ণ হওয়ার জন্য অপেক্ষা করতে হবে না।

যদি getSomeDate () getSomeOtherDate () তে কিছু সরবরাহ না করে, যা getMoreData () কে কিছুই সরবরাহ করে না তবে আপনি কেন তাদেরকে asynchronously হিসাবে জেএস হিসাবে অনুমতি দেয় না বা যদি তারা পরস্পরের উপর নির্ভরশীল (এবং অবিচ্ছিন্ন নয়) এগুলি লিখুন কেন? একক কাজ?

প্রবাহ নিয়ন্ত্রণ করতে আপনাকে নেস্টিং ব্যবহার করার দরকার নেই - উদাহরণস্বরূপ, প্রতিটি ফাংশন শেষ করার জন্য একটি সাধারণ ফাংশন কল করুন যা নির্ধারণ করে যে সমস্ত 3 টি শেষ হয়ে গেছে এবং তারপরে প্রতিক্রিয়া প্রেরণ করবে।


2

মনে করুন আপনি এটি করতে পারতেন:

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});
    var html = "<h1>Demo page</h1>";
    chain([
        function (next) {
            getSomeDate(client, next);
        },
        function (next, someData) {
            html += "<p>"+ someData +"</p>";
            getSomeOtherDate(client, next);
        },
        function (next, someOtherData) {
            html += "<p>"+ someOtherData +"</p>";
            getMoreData(client, next);
        },
        function (next, moreData) {
            html += "<p>"+ moreData +"</p>";
            res.write(html);
            res.end();
        }
    ]);
});

আপনাকে কেবল চেইন () প্রয়োগ করতে হবে যাতে এটি প্রতিটি ফাংশনটি আংশিকভাবে পরবর্তীটির সাথে প্রয়োগ করে এবং অবিলম্বে কেবলমাত্র প্রথম ক্রিয়াকলাপটি আহ্বান করে:

function chain(fs) {
    var f = function () {};
    for (var i = fs.length - 1; i >= 0; i--) {
        f = fs[i].partial(f);
    }
    f();
}

হাই ngn এবং উত্তরের জন্য ধন্যবাদ! আমার ক্ষেত্রে প্রতিটি ইনডেন্টেশনে ইনলাইন ক্লোজার ভেরিয়েবলের অ্যাক্সেস রয়েছে। উদাহরণস্বরূপ, ফাংশনগুলি এর মতো কাজ করে: এইচটিটিপি রেকর্ড / রেজোলিউশন পান, কুকির জন্য ডিবি থেকে ইউজারিড পান, পরবর্তী ব্যবহারকারীটির জন্য ইমেল পান, পরবর্তী ইমেলের জন্য আরও ডেটা পান, ..., পরবর্তী ওয়াইয়ের জন্য এক্স পান, ... যদি আমি ভুল না হয়ে থাকি তবে আপনার প্রস্তাবিত কোডটি কেবলমাত্র আশ্বাস দেয় যে অ্যাসিঙ্ক ফাংশনগুলি সঠিক ক্রমে কার্যকর করা হবে, তবে প্রতিটি ফাংশন বডিটিতে আমার মূল কোডটিতে ক্লোজারগুলি দ্বারা প্রাকৃতিকভাবে ভেরিয়েবল সরবরাহ করার উপায় নেই। এটাই কি?
কে প্যেল

2

কলব্যাক হেল্প বন্ধ হওয়ার সাথে সাথে খাঁটি জাভাস্ক্রিপ্টে সহজেই এড়ানো যায়। নীচের সমাধানটি সমস্ত কলব্যাকগুলি ফাংশন (ত্রুটি, ডেটা) স্বাক্ষর অনুসরণ করে।

http.createServer(function (req, res) {
  var modeNext, onNext;

  // closure variable to keep track of next-callback-state
  modeNext = 0;

  // next-callback-handler
  onNext = function (error, data) {
    if (error) {
      modeNext = Infinity;
    } else {
      modeNext += 1;
    }
    switch (modeNext) {

    case 0:
      res.writeHead(200, {'Content-Type': 'text/html'});
      var html = "<h1>Demo page</h1>";
      getSomeDate(client, onNext);
      break;

    // handle someData
    case 1:
        html += "<p>"+ data +"</p>";
        getSomeOtherDate(client, onNext);
        break;

    // handle someOtherData
    case 2:
      html += "<p>"+ data +"</p>";
      getMoreData(client, onNext);
      break;

    // handle moreData
    case 3:
      html += "<p>"+ data +"</p>";
      res.write(html);
      res.end();
      break;

    // general catch-all error-handler
    default:
      res.statusCode = 500;
      res.end(error.message + '\n' + error.stack);
    }
  };
  onNext();
});

1

আমি সম্প্রতি ওয়েট নামে সহজ বিমূর্ততা তৈরি করেছি sy সিঙ্ক মোডে অ্যাসিঙ্ক ফাংশনগুলি কল করতে ( ফাইবারের উপর ভিত্তি করে)। এটি প্রাথমিক পর্যায়ে তবে কাজ করে। এটা:

https://github.com/luciotato/waitfor

অপেক্ষা. for ব্যবহার করে , আপনি যে কোনও সিঙ্ক ফাংশন হিসাবে কোনও মানক নোডেজ অ্যাসিঙ্ক ফাংশনটি কল করতে পারেন।

আপনার কোডটি হতে পারে: ওয়েটার ব্যবহার করে :

var http=require('http');
var wait=require('wait.for');

http.createServer(function(req, res) {
  wait.launchFiber(handleRequest,req, res); //run in a Fiber, keep node spinning
}).listen(8080);


//in a fiber
function handleRequest(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  var someData = wait.for(getSomeDate,client);
  html += "<p>"+ someData +"</p>";
  var someOtherData = wait.for(getSomeOtherDate,client);
  html += "<p>"+ someOtherData +"</p>";
  var moreData = wait.for(getMoreData,client);
  html += "<p>"+ moreData +"</p>";
  res.write(html);
  res.end();
};

... বা আপনি যদি কম ভার্বোজ হতে চান (এবং ত্রুটি ধরাতে যুক্তও করুন)

//in a fiber
function handleRequest(req, res) {
  try {
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write(
    "<h1>Demo page</h1>" 
    + "<p>"+ wait.for(getSomeDate,client) +"</p>"
    + "<p>"+ wait.for(getSomeOtherDate,client) +"</p>"
    + "<p>"+ wait.for(getMoreData,client) +"</p>"
    );
    res.end();
  }
  catch(err) {
   res.end('error '+e.message); 
  }

};

সমস্ত ক্ষেত্রে, getSomeDate , getSomeOtherDate এবং getMoreData সর্বশেষ প্যারামিটারের সাথে একটি ফাংশন কলব্যাক ( ত্রুটি , ডেটা) স্ট্যান্ডার্ড অ্যাসিঙ্ক ফাংশন হওয়া উচিত

যেমন হিসাবে:

function getMoreData(client, callback){
  db.execute('select moredata from thedata where client_id=?',[client.id],
       ,function(err,data){
          if (err) callback(err);
          callback (null,data);
        });
}

1

এই সমস্যা সমাধানের জন্য আমি নোডেন্ট ( https://npmjs.org/package/nodent ) লিখেছি যা অদৃশ্যভাবে আপনার জেএসকে প্রাক-প্রক্রিয়াজাত করে। আপনার উদাহরণ কোড হয়ে উঠবে (অ্যাসিঙ্ক, সত্যই - দস্তাবেজগুলি পড়ুন)।

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  someData <<= getSomeDate(client) ;

  html += "<p>"+ someData +"</p>";
  someOtherData <<= getSomeOtherDate(client) ;

  html += "<p>"+ someOtherData +"</p>";
  moreData <<= getMoreData(client) ;

  html += "<p>"+ moreData +"</p>";
  res.write(html);
  res.end();
});

স্পষ্টতই, আরও অনেকগুলি সমাধান রয়েছে, তবে প্রাক-প্রসেসিংয়ে রান-টাইম ওভারহেড খুব কম বা না থাকার সুবিধা রয়েছে এবং উত্স-মানচিত্রের সমর্থনের জন্য ধন্যবাদ এটি খুব সহজেই ডিবাগ করা সহজ।


0

আমারও একই সমস্যা ছিল। আমি async ফাংশনগুলি নোড করার জন্য প্রধান লিবগুলি দেখেছি এবং তারা আপনার কোডটি তৈরি করতে এত প্রাকৃতিক চেইন (আপনার তিন বা ততোধিক পদ্ধতি ব্যবহার করতে হবে) উপস্থাপন করে।

আমি সহজ এবং পড়ার পক্ষে সহজ হওয়ার সমাধান বিকাশে কয়েক সপ্তাহ কাটিয়েছি। দয়া করে, EnqJS এ চেষ্টা করুন । সমস্ত মতামত প্রশংসা করা হবে।

পরিবর্তে:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";
  getSomeDate(client, function(someData) {
    html += "<p>"+ someData +"</p>";
    getSomeOtherDate(client, function(someOtherData) {
      html += "<p>"+ someOtherData +"</p>";
      getMoreData(client, function(moreData) {
        html += "<p>"+ moreData +"</p>";
        res.write(html);
        res.end();
      });
    });
  });

এনকিজেএস সহ:

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  var html = "<h1>Demo page</h1>";

  enq(function(){
    var self=this;
    getSomeDate(client, function(someData){
      html += "<p>"+ someData +"</p>";
      self.return();
    })
  })(function(){
    var self=this;
    getSomeOtherDate(client, function(someOtherData){ 
      html += "<p>"+ someOtherData +"</p>";
      self.return();
    })
  })(function(){
    var self=this;
    getMoreData(client, function(moreData) {
      html += "<p>"+ moreData +"</p>";
      self.return();
      res.write(html);
      res.end();
    });
  });
});

কোডটি আগের চেয়ে বড় বলে মনে হচ্ছে। তবে আগের মতো বাসা বাঁধেনি। আরও প্রাকৃতিক প্রদর্শিত, চেইনগুলি উদ্দেশ্যহীনভাবে বলা হয়:

enq(fn1)(fn2)(fn3)(fn4)(fn4)(...)

এবং এটি ফিরে এসেছিল বলে, আমরা যে ফাংশনটি কল করি:

this.return(response)

0

আমি এটি একটি আদিম কিন্তু কার্যকর উপায়ে করি। উদাহরণস্বরূপ, আমি এর পিতা-মাতা এবং বাচ্চাদের সাথে আমার একটি মডেল পেতে চাই এবং বলি যে আমি তাদের জন্য পৃথক অনুসন্ধান করা দরকার:

var getWithParents = function(id, next) {
  var getChildren = function(model, next) {
        /*... code ... */
        return next.pop()(model, next);
      },
      getParents = function(model, next) {
        /*... code ... */
        return next.pop()(model, next);
      }
      getModel = function(id, next) {
        /*... code ... */
        if (model) {
          // return next callbacl
          return next.pop()(model, next);
        } else {
          // return last callback
          return next.shift()(null, next);
        }
      }

  return getModel(id, [getParents, getChildren, next]);
}

0

ফাইবার ব্যবহার করুন https://github.com/laverdet/node-fibers এটি অ্যাসিঙ্ক্রোনাস কোডটিকে সিনক্রোনাসের মতো দেখায় (অবরুদ্ধ না করে)

আমি ব্যক্তিগতভাবে এই প্রচ্ছদটি http://alexeypetrushin.github.com/synchronize আমার প্রকল্পের কোডের নমুনা ব্যবহার করি (প্রতিটি পদ্ধতি আসলে অ্যাসিঙ্ক্রোনাস হয়, অ্যাসিঙ্ক ফাইল আইও নিয়ে কাজ করে) আমি কল্পনা করেও ভয় পাই যে কলব্যাকের সাথে এটি কী গণ্ডগোল হবে or অ্যাসিঙ্ক-নিয়ন্ত্রণ-প্রবাহ সহায়তা সহায়ক লাইব্রেরি।

_update: (version, changesBasePath, changes, oldSite) ->
  @log 'updating...'
  @_updateIndex version, changes
  @_updateFiles version, changesBasePath, changes
  @_updateFilesIndexes version, changes
  configChanged = @_updateConfig version, changes
  @_updateModules version, changes, oldSite, configChanged
  @_saveIndex version
  @log "updated to #{version} version"

0

Task.js আপনাকে এই অফার করে:

spawn(function*() {
    try {
        var [foo, bar] = yield join(read("foo.json"),
                                    read("bar.json")).timeout(1000);
        render(foo);
        render(bar);
    } catch (e) {
        console.log("read failed: " + e);
    }
});

এর পরিবর্তে:

var foo, bar;
var tid = setTimeout(function() { failure(new Error("timed out")) }, 1000);

var xhr1 = makeXHR("foo.json",
                   function(txt) { foo = txt; success() },
                   function(err) { failure() });
var xhr2 = makeXHR("bar.json",
                   function(txt) { bar = txt; success() },
                   function(e) { failure(e) });

function success() {
    if (typeof foo === "string" && typeof bar === "string") {
        cancelTimeout(tid);
        xhr1 = xhr2 = null;
        render(foo);
        render(bar);
    }
}

function failure(e) {
    xhr1 && xhr1.abort();
    xhr1 = null;
    xhr2 && xhr2.abort();
    xhr2 = null;
    console.log("read failed: " + e);
}

0

অন্যরা প্রতিক্রিয়া জানানোর পরে, আপনি বলেছিলেন যে আপনার সমস্যাটি স্থানীয় পরিবর্তনশীল were এটি করার একটি সহজ উপায় বলে মনে হচ্ছে এটি স্থানীয় ভেরিয়েবলগুলি ধারণ করতে একটি বহিরাগত ফাংশন লিখুন, তারপরে নামকরণ করা অভ্যন্তরীণ ফাংশনগুলির একটি গোছা ব্যবহার করুন এবং নাম দ্বারা এগুলি অ্যাক্সেস করুন। আপনার একসাথে কতগুলি কার্যকারিতা প্রয়োজন তা নির্বিশেষে আপনি কেবল দুটি গভীর বাসা বাঁধবেন।

mysqlনীড়ের সাথে নোড.জেএস মডিউলটি ব্যবহার করার জন্য আমার নবাগতের চেষ্টা এখানে :

function with_connection(sql, bindings, cb) {
    pool.getConnection(function(err, conn) {
        if (err) {
            console.log("Error in with_connection (getConnection): " + JSON.stringify(err));
            cb(true);
            return;
        }
        conn.query(sql, bindings, function(err, results) {
            if (err) {
                console.log("Error in with_connection (query): " + JSON.stringify(err));
                cb(true);
                return;
            }
            console.log("with_connection results: " + JSON.stringify(results));
            cb(false, results);
        });
    });
}

নীচের নামকরণ করা অভ্যন্তরীণ ফাংশন ব্যবহার করে একটি পুনর্লিখন। বহিরাগত ফাংশনটি with_connectionস্থানীয় ভেরিয়েবলগুলির ধারক হিসাবেও ব্যবহার করা যেতে পারে। (এখানে, আমি পরামিতি পেয়েছেন sql, bindings, cbঠিক একই ভাবে, যে আইন, কিন্তু আপনি শুধু কিছু অতিরিক্ত স্থানীয় ভেরিয়েবল বর্ণনা করতে পারেন with_connection।)

function with_connection(sql, bindings, cb) {

    function getConnectionCb(err, conn) {
        if (err) {
            console.log("Error in with_connection/getConnectionCb: " + JSON.stringify(err));
            cb(true);
            return;
        }
        conn.query(sql, bindings, queryCb);
    }

    function queryCb(err, results) {
        if (err) {
            console.log("Error in with_connection/queryCb: " + JSON.stringify(err));
            cb(true);
            return;
        }
        cb(false, results);
    }

    pool.getConnection(getConnectionCb);
}

আমি ভাবছিলাম যে সম্ভবত উদাহরণের ভেরিয়েবলগুলির সাহায্যে কোনও বস্তু তৈরি করা সম্ভব হবে এবং স্থানীয় ভেরিয়েবলগুলির প্রতিস্থাপন হিসাবে এই উদাহরণের ভেরিয়েবলগুলি ব্যবহার করা সম্ভব হবে। তবে এখন আমি দেখতে পেয়েছি যে নেস্টেড ফাংশন এবং স্থানীয় ভেরিয়েবলগুলি ব্যবহার করে উপরের পদ্ধতিটি সহজ এবং বোঝা সহজ। ও-কে প্রকাশ করতে কিছুটা সময় নেয়, দেখে মনে হয় :-)

সুতরাং এখানে একটি পূর্ববর্তী সংস্করণ একটি অবজেক্ট এবং উদাহরণ ভেরিয়েবল সঙ্গে।

function DbConnection(sql, bindings, cb) {
    this.sql = sql;
    this.bindings = bindings;
    this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
    var self = this;
    if (err) {
        console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    conn.query(this.sql, this.bindings, function(err, results) { self.query(err, results); });
}
DbConnection.prototype.query = function(err, results) {
    var self = this;
    if (err) {
        console.log("Error in DbConnection.query: " + JSON.stringify(err));
        self.cb(true);
        return;
    }
    console.log("DbConnection results: " + JSON.stringify(results));
    self.cb(false, results);
}

function with_connection(sql, bindings, cb) {
    var dbc = new DbConnection(sql, bindings, cb);
    pool.getConnection(function (err, conn) { dbc.getConnection(err, conn); });
}

দেখা যাচ্ছে যে bindকিছু সুবিধা হিসাবে ব্যবহার করা যেতে পারে। এটি আমাকে এমন কোনও কুৎসিত বেনাম ফাংশনগুলি থেকে মুক্ত করতে সহায়তা করে যা একটি পদ্ধতি কলটিতে নিজেকে ফরোয়ার্ড করা বাদ দিয়ে খুব বেশি কিছু করেনি। আমি পদ্ধতিটি সরাসরি পাস করতে পারিনি কারণ এটির ভুল মূল্যের সাথে জড়িত ছিল this। তবে এর সাথে bind, আমি thisযেটি চাই তার মান নির্দিষ্ট করতে পারি।

function DbConnection(sql, bindings, cb) {
    this.sql = sql;
    this.bindings = bindings;
    this.cb = cb;
}
DbConnection.prototype.getConnection = function(err, conn) {
    var f = this.query.bind(this);
    if (err) {
        console.log("Error in DbConnection.getConnection: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    conn.query(this.sql, this.bindings, f);
}
DbConnection.prototype.query = function(err, results) {
    if (err) {
        console.log("Error in DbConnection.query: " + JSON.stringify(err));
        this.cb(true);
        return;
    }
    console.log("DbConnection results: " + JSON.stringify(results));
    this.cb(false, results);
}

// Get a connection from the pool, execute `sql` in it
// with the given `bindings`.  Invoke `cb(true)` on error,
// invoke `cb(false, results)` on success.  Here,
// `results` is an array of results from the query.
function with_connection(sql, bindings, cb) {
    var dbc = new DbConnection(sql, bindings, cb);
    var f = dbc.getConnection.bind(dbc);
    pool.getConnection(f);
}

অবশ্যই, এর কোনওটিই নোড.জেএস কোডিংয়ের সাথে উপযুক্ত জেএস নয় - আমি এটিতে কয়েক ঘন্টা ব্যয় করেছি। তবে সম্ভবত কিছুটা পালিশ করে এই কৌশলটি সাহায্য করতে পারে?


0

async.js এর জন্য ভাল কাজ করে। আমি এই খুব দরকারী নিবন্ধটি জুড়ে এসেছি যা উদাহরণগুলির সাথে async.js এর প্রয়োজনীয়তা এবং ব্যবহারের ব্যাখ্যা দেয়: http://www.sebastianseilund.com/nodejs-async-in- অনুশীলন


0

আপনি যদি "পদক্ষেপ" বা "সিক" ব্যবহার করতে না চান তবে দয়া করে "লাইন" চেষ্টা করুন যা নেস্টেড অ্যাসিঙ্ক কলব্যাক হ্রাস করার জন্য একটি সাধারণ কাজ।

https://github.com/kevin0571/node-line



0

ব্যবহার টেলিগ্রাম আপনার কোড ভালো দেখাবে:

http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/html'});

    var l = new Wire();

    getSomeDate(client, l.branch('someData'));
    getSomeOtherDate(client, l.branch('someOtherData'));
    getMoreData(client, l.branch('moreData'));

    l.success(function(r) {
        res.write("<h1>Demo page</h1>"+
            "<p>"+ r['someData'] +"</p>"+
            "<p>"+ r['someOtherData'] +"</p>"+
            "<p>"+ r['moreData'] +"</p>");
        res.end();
    });
});

0

আপনার জানার জন্য Jazz.js https://github.com/Javanile/Jazz.js/wiki/Script-Sow کیس বিবেচনা করুন

    কনস্ট জেজে = প্রয়োজনীয় ('jazz.js');

    // আল্ট্রা-কম্প্যাট স্ট্যাক
    jj.script ([
        a => প্রসেসটাস্কঅনক্যালব্যাকআউটএন্ড (ক),
        b => প্রসেসটি টাস্কটলক্যালব্যাকআউটএন্ড (খ),
        c => প্রসেসটি টাস্কট্রিকলব্যাকএটিএন্ড (সি),
        d => প্রসেসটাস্কফোরক্যালব্যাকআউটএন্ড (d),
        e => প্রসেসটাস্কফাইভক্যালব্যাকআউটএন্ড (ই),
    ]);

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.