সমস্ত কলমাপূর্ণ সকল কলব্যাক সমাপ্ত হওয়ার পরে কলব্যাক


245

শিরোনাম হিসাবে প্রস্তাব। আমি এটা কিভাবে করবো?

whenAllDone()ফর-ই-লুপ প্রতিটি উপাদান পেরিয়ে কিছু অ্যাসিনক্রোনাস প্রসেসিংয়ের পরে কল করতে চাই ।

[1, 2, 3].forEach(
  function(item, index, array, done) {
     asyncFunction(item, function itemDone() {
       console.log(item + " done");
       done();
     });
  }, function allDone() {
     console.log("All done");
     whenAllDone();
  }
);

এটি কি এভাবে কাজ করা সম্ভব? ForEach এর দ্বিতীয় যুক্তিটি যখন কলব্যাক ফাংশন হয় যা একবারে সমস্ত পুনরাবৃত্তির মধ্য দিয়ে চলে?

প্রত্যাশিত আউটপুট:

3 done
1 done
2 done
All done!

13
স্ট্যান্ডার্ড অ্যারে forEachপদ্ধতিতে doneকলব্যাক প্যারামিটার এবং allDoneকলব্যাক থাকলে এটি দুর্দান্ত হবে!
ভানুয়ান

22
এটি সত্যিকারের লজ্জাজনক বিষয়টির জন্য জাভাস্ক্রিপ্টে এতটা কুস্তি হওয়া প্রয়োজন।
আলী

উত্তর:


410

Array.forEach এই নব্বইটি সরবরাহ করে না (ওহ যদি তা হয় তবে) আপনি যা চান তা অর্জন করার বিভিন্ন উপায় রয়েছে:

একটি সাধারণ কাউন্টার ব্যবহার করে

function callback () { console.log('all done'); }

var itemsProcessed = 0;

[1, 2, 3].forEach((item, index, array) => {
  asyncFunction(item, () => {
    itemsProcessed++;
    if(itemsProcessed === array.length) {
      callback();
    }
  });
});

(@ ভানুয়ান এবং অন্যান্যদের জন্য ধন্যবাদ) এই পদ্ধতির গ্যারান্টি দেয় যে "সম্পন্ন" কলব্যাক শুরু করার আগে সমস্ত আইটেম প্রক্রিয়া করা হবে। আপনার একটি কাউন্টার ব্যবহার করা দরকার যা কলব্যাকে আপডেট হয়। সূচকের প্যারামিটারের মানের উপর নির্ভর করে একই গ্যারান্টি সরবরাহ করে না, কারণ অ্যাসিঙ্ক্রোনাস ক্রিয়াকলাপের ফেরতের আদেশের নিশ্চয়তা নেই।

ES6 প্রতিশ্রুতি ব্যবহার করে

(একটি প্রতিশ্রুতি গ্রন্থাগার পুরানো ব্রাউজারগুলির জন্য ব্যবহার করা যেতে পারে):

  1. সিঙ্ক্রোনাস এক্সিকিউশন গ্যারান্টিযুক্ত সমস্ত অনুরোধগুলি প্রক্রিয়া করুন (উদাহরণস্বরূপ 1 তারপর 2 তারপর 3)

    function asyncFunction (item, cb) {
      setTimeout(() => {
        console.log('done with', item);
        cb();
      }, 100);
    }
    
    let requests = [1, 2, 3].reduce((promiseChain, item) => {
        return promiseChain.then(() => new Promise((resolve) => {
          asyncFunction(item, resolve);
        }));
    }, Promise.resolve());
    
    requests.then(() => console.log('done'))
  2. "সিঙ্ক্রোনাস" এক্সিকিউশন ছাড়াই সমস্ত অ্যাসিঙ্ক অনুরোধগুলি প্রক্রিয়া করুন (2 টি 1 এর চেয়ে দ্রুত শেষ করতে পারে)

    let requests = [1,2,3].map((item) => {
        return new Promise((resolve) => {
          asyncFunction(item, resolve);
        });
    })
    
    Promise.all(requests).then(() => console.log('done'));

একটি অ্যাসিঙ্ক লাইব্রেরি ব্যবহার করা হচ্ছে

অন্যান্য অ্যাসিঙ্ক্রোনাস লাইব্রেরি, হয় ASYNC সবচেয়ে জনপ্রিয় হচ্ছে, যে মেকানিজম কি আপনি চান প্রকাশ করার প্রদান।

সম্পাদন করা

পূর্বের সিঙ্ক্রোনাসের উদাহরণ কোডটি সরিয়ে দিতে প্রশ্নের মূল অংশ সম্পাদনা করা হয়েছে, সুতরাং আমি আমার উত্তরটি পরিষ্কার করতে আপডেট করেছি। আসল উদাহরণটি সিঙ্ক্রোনাসের মতো কোডটি অ্যাসিক্রোনাস আচরণের মডেল করতে ব্যবহৃত হয়েছিল, সুতরাং নিম্নলিখিতটি প্রয়োগ করা হয়েছে:

array.forEachহয় সমকালীন এবং তাই হয় res.write, তাই আপনি কেবল foreach আপনার কল করার পর আপনার কলব্যাক লাগাতে পারেন:

  posts.foreach(function(v, i) {
    res.write(v + ". index " + i);
  });

  res.end();

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

একটি লুপে একটি অ্যাসিঙ্ক
ক্রিয়া

2
@ ভানুয়ান আমি আপনার পরিবর্তে উল্লেখযোগ্য সম্পাদনার চেয়ে আরও ভালভাবে মেলে আমার উত্তর আপডেট করেছি :)
নিক টমলিন

4
কেন শুধু if(index === array.length - 1)সরান এবং সরান নাitemsProcessed
আমিন জাফারি 21

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

25

আপনি যদি অ্যাসিক্রোনাস ফাংশনগুলির মুখোমুখি হন এবং আপনি নিশ্চিত করতে চান যে কোডটি কার্যকর করার আগে এটি এর কাজ শেষ করে, আমরা সর্বদা কলব্যাক ক্ষমতাটি ব্যবহার করতে পারি।

উদাহরণ স্বরূপ:

var ctr = 0;
posts.forEach(function(element, index, array){
    asynchronous(function(data){
         ctr++; 
         if (ctr === array.length) {
             functionAfterForEach();
         }
    })
});

দ্রষ্টব্য: functionAfterForEachফোরচ কাজ শেষ হওয়ার পরে সম্পাদন করা ফাংশন। asynchronousপূর্বাভাসের ভিতরে সম্পাদিত অ্যাসিক্রোনাস ফাংশন।


9
এটি কাজ করবে না যেহেতু অ্যাসিঙ্ক্রোনাস অনুরোধগুলি কার্যকর করার ক্রমটি সন্ধান করা হয়নি। শেষ অ্যাসিঙ্ক অনুরোধটি অন্যদের আগে শেষ হয়ে যেতে পারে এবং সমস্ত অনুরোধগুলি শেষ হওয়ার আগে ফাংশনএফটারফরএচ () সম্পাদন করতে পারে।
র‌্যামি ডেভিড

@ রেমিডাভিড হ্যাঁ, মৃত্যুদন্ড কার্যকর করার আদেশ সম্পর্কে আপনার কাছে একটি বক্তব্য রয়েছে বা আমি বলব প্রসেসটি কতক্ষণ শেষ হয় তবে জাভাস্ক্রিপ্ট এককভাবে থ্রেডড হয়ে গেছে যা শেষ পর্যন্ত এটি কাজ করে। এবং প্রমাণটি এই উত্তরটি পেয়েছে up
এমিল রেয়া এনরিকিউজ

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

2
হ্যাঁ, এই উত্তরটি সম্পূর্ণ ভুল। যদি আমি সমান্তরালভাবে 10 ডাউনলোডগুলি চালনা করি তবে এগুলি সবই গ্যারান্টিযুক্ত যে শেষ ডাউনলোডটি বাকিগুলির আগে শেষ হয়ে যায় এবং ফলস্বরূপ কার্যকর করা বন্ধ করে দেয়।
knrdk

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

17

আশা করি এটি আপনার সমস্যার সমাধান করবে, আমি যখন ভিতরে ভিতরে অ্যাসিক্রোনাস টাস্ক সহ প্রতিটি জন্য এক্সিকিউট করতে হয় তখন আমি সাধারণত এটি নিয়ে কাজ করি।

foo = [a,b,c,d];
waiting = foo.length;
foo.forEach(function(entry){
      doAsynchronousFunction(entry,finish) //call finish after each entry
}
function finish(){
      waiting--;
      if (waiting==0) {
          //do your Job intended to be done after forEach is completed
      } 
}

সঙ্গে

function doAsynchronousFunction(entry,callback){
       //asynchronousjob with entry
       callback();
}

আমার কৌনিক 9 কোডে আমার একই রকম সমস্যা ছিল এবং এই উত্তরটি আমার পক্ষে কৌশলটি করেছে। যদিও এমিল রেইয়া এনরিকিউজ উত্তরটি আমার পক্ষে কাজ করেছে তবে আমি এই সমস্যার আরও সঠিক এবং সহজ উত্তর বলে মনে করি।
সর্বমোট

17

এটি কতটা ভুল উত্তর দেওয়া হয়েছে তা আজব অ্যাসিক্রোনাস মামলায় ! এটি সহজভাবে দেখানো যেতে পারে যে পরীক্ষণ সূচকটি প্রত্যাশিত আচরণ সরবরাহ করে না:

// INCORRECT
var list = [4000, 2000];
list.forEach(function(l, index) {
    console.log(l + ' started ...');
    setTimeout(function() {
        console.log(index + ': ' + l);
    }, l);
});

আউটপুট:

4000 started
2000 started
1: 2000
0: 4000

আমরা যদি পরীক্ষা করে দেখি index === array.length - 1, প্রথম পুনরাবৃত্তি সম্পন্ন হওয়ার পরে কলব্যাক কল করা হবে, যদিও প্রথম উপাদানটি এখনও মুলতুবি রয়েছে!

অ্যাসিঙ্কের মতো বাহ্যিক গ্রন্থাগারগুলি ব্যবহার না করেই এই সমস্যাটি সমাধান করার জন্য, আমি মনে করি আপনার সেরা বেট হ'ল প্রতিটি পুনরাবৃত্তির পরে যদি তালিকা এবং হ্রাসের দৈর্ঘ্য সংরক্ষণ করা হয়। যেহেতু কেবলমাত্র একটি থ্রেড আমরা নিশ্চিত যে রেসের শর্তের কোনও সম্ভাবনা নেই।

var list = [4000, 2000];
var counter = list.length;
list.forEach(function(l, index) {
    console.log(l + ' started ...');
    setTimeout(function() {
        console.log(index + ': ' + l);
        counter -= 1;
        if ( counter === 0)
            // call your callback here
    }, l);
});

1
সম্ভবত এটিই একমাত্র সমাধান। অ্যাসিঙ্ক লাইব্রেরিও কি কাউন্টার ব্যবহার করে?
ভানুয়ান

1
যদিও অন্যান্য সমাধানগুলি কাজটি করে তবে এটি সর্বাধিক আকর্ষণীয় কারণ এর জন্য শৃঙ্খলাবদ্ধতা বা যুক্ত জটিলতার প্রয়োজন হয় না। KISS
আজাতর

অ্যারের দৈর্ঘ্য শূন্য হলে পরিস্থিতিটিও বিবেচনা করুন, এক্ষেত্রে কলব্যাক কখনও কল করা হবে না
সাইদ ইর

6

ES2018 এর সাহায্যে আপনি async পুনরুক্তি ব্যবহার করতে পারেন:

const asyncFunction = a => fetch(a);
const itemDone = a => console.log(a);

async function example() {
  const arrayOfFetchPromises = [1, 2, 3].map(asyncFunction);

  for await (const item of arrayOfFetchPromises) {
    itemDone(item);
  }

  console.log('All done');
}

1
নোড
ভি

2

প্রতিশ্রুতি ব্যতীত আমার সমাধান (এটি নিশ্চিত করে যে প্রতিটি পদক্ষেপ পরবর্তী কাজ শুরু হওয়ার আগেই শেষ হয়ে যায়):

Array.prototype.forEachAsync = function (callback, end) {
        var self = this;
    
        function task(index) {
            var x = self[index];
            if (index >= self.length) {
                end()
            }
            else {
                callback(self[index], index, self, function () {
                    task(index + 1);
                });
            }
        }
    
        task(0);
    };
    
    
    var i = 0;
    var myArray = Array.apply(null, Array(10)).map(function(item) { return i++; });
    console.log(JSON.stringify(myArray));
    myArray.forEachAsync(function(item, index, arr, next){
      setTimeout(function(){
        $(".toto").append("<div>item index " + item + " done</div>");
        console.log("action " + item + " done");
        next();
      }, 300);
    }, function(){
        $(".toto").append("<div>ALL ACTIONS ARE DONE</div>");
        console.log("ALL ACTIONS ARE DONE");
    });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="toto">

</div>


1
 var counter = 0;
 var listArray = [0, 1, 2, 3, 4];
 function callBack() {
     if (listArray.length === counter) {
         console.log('All Done')
     }
 };
 listArray.forEach(function(element){
     console.log(element);
     counter = counter + 1;
     callBack();
 });

1
এটি কাজ করবে না কারণ যদি আপনার পূর্বাভাসের ভিতরে অ্যাসিঙ্ক অপারেশন থাকে।
সুধাংশু গৌড়


0

আমার সমাধান:

//Object forEachDone

Object.defineProperty(Array.prototype, "forEachDone", {
    enumerable: false,
    value: function(task, cb){
        var counter = 0;
        this.forEach(function(item, index, array){
            task(item, index, array);
            if(array.length === ++counter){
                if(cb) cb();
            }
        });
    }
});


//Array forEachDone

Object.defineProperty(Object.prototype, "forEachDone", {
    enumerable: false,
    value: function(task, cb){
        var obj = this;
        var counter = 0;
        Object.keys(obj).forEach(function(key, index, array){
            task(obj[key], key, obj);
            if(array.length === ++counter){
                if(cb) cb();
            }
        });
    }
});

উদাহরণ:

var arr = ['a', 'b', 'c'];

arr.forEachDone(function(item){
    console.log(item);
}, function(){
   console.log('done');
});

// out: a b c done

সমাধানটি উদ্ভাবনী তবে একটি ত্রুটি আসছে - "টাস্ক কোনও ফাংশন নয়"
জিনিয়াস

0

আমি এটি সমাধান করার সহজ উপায় চেষ্টা করি, এটি আপনার সাথে শেয়ার করুন:

let counter = 0;
            arr.forEach(async (item, index) => {
                await request.query(item, (err, recordset) => {
                    if (err) console.log(err);

                    //do Somthings

                    counter++;
                    if(counter == tableCmd.length){
                        sql.close();
                        callback();
                    }
                });

requestনোড জেএস এর এমএসকিএল লাইব্রেরির কাজ। এটি প্রতিটি ফাংশন বা কোড আপনার চাইলে প্রতিস্থাপন করতে পারে। গুডলাক


0
var i=0;
const waitFor = (ms) => 
{ 
  new Promise((r) => 
  {
   setTimeout(function () {
   console.log('timeout completed: ',ms,' : ',i); 
     i++;
     if(i==data.length){
      console.log('Done')  
    }
  }, ms); 
 })
}
var data=[1000, 200, 500];
data.forEach((num) => {
  waitFor(num)
})

-2

তালিকার মাধ্যমে পুনরাবৃত্তি করার জন্য আপনার কলব্যাকের প্রয়োজন হবে না। end()লুপের পরে কেবল কলটি যুক্ত করুন ।

posts.forEach(function(v, i){
   res.write(v + ". Index " + i);
});
res.end();

3
না, ওপি জোর দিয়েছিল যে প্রতিটি পুনরাবৃত্তির জন্য অ্যাসিনক্রোনাস যুক্তি কার্যকর করা হবে। res.writeঅ্যাসিক্রোনাস অপারেশন নয়, সুতরাং আপনার কোড কাজ করবে না।
জিম জি।

-2

একটি সহজ সমাধান অনুসরণ মত হবে

function callback(){console.log("i am done");}

["a", "b", "c"].forEach(function(item, index, array){
    //code here
    if(i == array.length -1)
    callback()
}

3
অ্যাসিক্রোনাস কোডের জন্য কাজ করে না যা প্রশ্নের পুরো ভিত্তি।
গ্রিগ করুন

-3

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

_.forEach(actual_JSON, function (key, value) {

     // run any action and push with each iteration 

     array.push(response.id)

});


setInterval(function(){

    if(array.length > 300) {

        callback()

    }

}, 100);

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