संकल्प एक के बाद एक वादे (क्रम में)?


269

निम्नलिखित कोड पर विचार करें जो धारावाहिक / अनुक्रमिक तरीके से फ़ाइलों की एक सरणी को पढ़ता है। readFilesएक वादा लौटाता है, जिसे केवल एक बार सभी फाइलों को अनुक्रम में पढ़ने के बाद हल किया जाता है।

var readFile = function(file) {
  ... // Returns a promise.
};

var readFiles = function(files) {
  return new Promise((resolve, reject) => 

    var readSequential = function(index) {
      if (index >= files.length) {
        resolve();
      } else {
        readFile(files[index]).then(function() {
          readSequential(index + 1);
        }).catch(reject);
      }
    };

   readSequential(0); // Start!

  });
};

उपरोक्त कोड काम करता है, लेकिन मुझे क्रमिक रूप से होने वाली चीजों के लिए पुनरावृत्ति करना पसंद नहीं है। क्या एक सरल तरीका है कि इस कोड को फिर से लिखा जा सकता है ताकि मुझे अपने अजीब readSequentialफ़ंक्शन का उपयोग न करना पड़े ?

मूल रूप से मैंने उपयोग करने की कोशिश की Promise.all, लेकिन इससे readFileकॉल के सभी समवर्ती हो गए, जो कि मैं नहीं चाहता हूं:

var readFiles = function(files) {
  return Promise.all(files.map(function(file) {
    return readFile(file);
  }));
};

2
कुछ भी जिसे खत्म करने के लिए पिछले अतुल्यकालिक ऑपरेशन के लिए इंतजार करना पड़ता है उसे कॉलबैक में करना पड़ता है। वादों के प्रयोग से ऐसा नहीं होता। तो आपको पुनरावृत्ति की आवश्यकता है।
बारमर

1
FYI करें, यह तकनीकी रूप से पुनरावृत्ति नहीं है क्योंकि स्टैक फ्रेम बिल्ड-अप नहीं है। पिछला पहले readFileSequential()से ही अगले एक को बुलाए जाने से पहले वापस आ गया है (क्योंकि यह async है, यह मूल फ़ंक्शन कॉल पहले ही वापस आ जाने के बाद लंबे समय तक पूरा हो जाता है)।
१५

1
@ jfriend00 पुनरावर्तन के लिए स्टैक फ्रेम संचय की आवश्यकता नहीं है - केवल आत्म संदर्भ। यह हालांकि एक तकनीकीता है।
बेंजामिन ग्रुएनबाम

3
@BenjaminGruenbaum - मेरी बात यह है कि अगले कॉल को बंद करने के लिए फंक्शन कॉल के होने में कुछ भी गलत नहीं है। इसके साथ शून्य नीचे है और, वास्तव में, यह async संचालन अनुक्रम करने के लिए एक कुशल तरीका है। तो, ऐसा कुछ भी बचने का कोई कारण नहीं है जो पुनरावृत्ति जैसा दिखता है। कुछ समस्याओं के पुनरावर्ती समाधान हैं जो अक्षम हैं - यह उनमें से एक नहीं है।
18

1
अरे, जावास्क्रिप्ट कमरे में एक चर्चा और अनुरोध के अनुसार मैंने इस उत्तर को संपादित किया है ताकि हम दूसरों को एक विहित के रूप में इंगित कर सकें। यदि आप असहमत हैं तो कृपया मुझे बताएं और मैं इसे पुनर्स्थापित कर दूंगा और एक अलग खोल दूंगा।
बेंजामिन ग्रुएनबाम

जवाबों:


337

अपडेट 2017 : अगर पर्यावरण इसका समर्थन करता है तो मैं एक एसिंक्स फ़ंक्शन का उपयोग करूंगा:

async function readFiles(files) {
  for(const file of files) {
    await readFile(file);
  }
};

यदि आप चाहें, तो आप फ़ाइलों को तब तक पढ़ना बंद कर सकते हैं जब तक आपको उनकी आवश्यकता नहीं होती है कि वे एक एसिंक्स जनरेटर का उपयोग करें (यदि आपका पर्यावरण इसका समर्थन करता है):

async function* readFiles(files) {
  for(const file of files) {
    yield await readFile(file);
  }
};

अद्यतन: दूसरे विचार में - मैं इसके बजाय लूप के लिए उपयोग कर सकता हूं:

var readFiles = function(files) {
  var p = Promise.resolve(); // Q() in q

  files.forEach(file =>
      p = p.then(() => readFile(file)); 
  );
  return p;
};

या अधिक कॉम्पैक्ट, कम करने के साथ:

var readFiles = function(files) {
  return files.reduce((p, file) => {
     return p.then(() => readFile(file));
  }, Promise.resolve()); // initial
};

अन्य वादा पुस्तकालयों में (जैसे कब और ब्लूबर्ड) आपके पास इसके लिए उपयोगिता विधियां हैं।

उदाहरण के लिए, ब्लूबर्ड होगा:

var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));

var readAll = Promise.resolve(files).map(fs.readFileAsync,{concurrency: 1 });
// if the order matters, you can use Promise.each instead and omit concurrency param

readAll.then(function(allFileContents){
    // do stuff to read files.
});

हालाँकि आज वास्तव में async प्रतीक्षा का उपयोग नहीं करने का कोई कारण नहीं है।


2
@ EmreTapcı, nope। एक तीर फ़ंक्शन का "=>" पहले से ही लौटने का अर्थ है।
अधिकतम

यदि आप टाइपस्क्रिप्ट का उपयोग करते हैं, तो मुझे लगता है कि "इन" लूप सॉल्यूशन सबसे अच्छा है। रिटर्न कम करें पुनरावर्ती वादे जैसे। पहला कॉल रिटर्न टाइप प्रोमिस <void> है, फिर दूसरा है प्रोमिस <प्रॉमिस <शून्य >> और इसी तरह - किसी भी प्रकार का उपयोग किए बिना टाइप करना असंभव है, मुझे लगता है
आर्टूर टैगिसो

@ArturTagisow टाइपस्क्रिप्ट (कम से कम नए संस्करण) में पुनरावर्ती प्रकार होते हैं और उन्हें यहां सही प्रकार से हल करना चाहिए । "पुनरावृत्ति को आत्मसात" करने के वादे के बाद से एक वादा के रूप में ऐसी कोई बात नहीं है। Promise.resolve(Promise.resolve(15))के समान है Promise.resolve(15)
बेंजामिन ग्रुएनबाम


72

यहां बताया गया है कि मैं श्रृंखला में कार्यों को कैसे चलाना पसंद करता हूं।

function runSerial() {
    var that = this;
    // task1 is a function that returns a promise (and immediately starts executing)
    // task2 is a function that returns a promise (and immediately starts executing)
    return Promise.resolve()
        .then(function() {
            return that.task1();
        })
        .then(function() {
            return that.task2();
        })
        .then(function() {
            console.log(" ---- done ----");
        });
}

अधिक कार्यों वाले मामलों के बारे में क्या? जैसे, १०?

function runSerial(tasks) {
  var result = Promise.resolve();
  tasks.forEach(task => {
    result = result.then(() => task());
  });
  return result;
}

8
और उन मामलों के बारे में क्या है जहाँ आप कार्यों की सही संख्या नहीं जानते हैं?
13

1
और क्या होगा जब आप कार्यों की संख्या जानते हैं, लेकिन केवल रनटाइम पर?
joeytwiddle

10
"आप वादों की एक सरणी से अधिक काम नहीं करना चाहते हैं। वादे की कल्पना के अनुसार, जैसे ही एक वादा किया जाता है, यह निष्पादित करना शुरू कर देता है। इसलिए जो आप वास्तव में चाहते हैं वह वादा कारखानों का एक सरणी है" उन्नत गलती देखें # 3 यहाँ: pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
edelans

5
यदि आप लाइन के शोर को कम कर रहे हैं, तो आप यह भी लिख सकते हैंresult = result.then(task);
डैनियल बकमास्टर

1
@DanielBuckmaster हाँ, लेकिन सावधान रहें, क्योंकि यदि कार्य () एक मान लौटाता है, तो इसे अगले आह्वान पर पास किया जाएगा। यदि आपके कार्य में वैकल्पिक तर्क हैं, तो इसके दुष्प्रभाव हो सकते हैं। वर्तमान कोड परिणाम निगलता है और स्पष्ट रूप से अगले कार्य को बिना किसी तर्क के लागू करता है।
जेएचएच

63

यह प्रश्न पुराना है, लेकिन हम ES6 और कार्यात्मक जावास्क्रिप्ट की दुनिया में रहते हैं, तो आइए देखें कि हम कैसे सुधार कर सकते हैं।

क्योंकि वादे तुरंत निष्पादित होते हैं, हम सिर्फ वादों की एक सरणी नहीं बना सकते, वे सभी समानांतर में आग लगा देंगे।

इसके बजाय, हमें वादों को पूरा करने वाले कार्यों की एक सरणी बनाने की आवश्यकता है। प्रत्येक फ़ंक्शन को तब क्रमिक रूप से निष्पादित किया जाएगा, जो उसके बाद वादा शुरू करता है।

हम इसे कुछ तरीकों से हल कर सकते हैं, लेकिन मेरा पसंदीदा तरीका उपयोग करना है reduce

reduceवादों के संयोजन में इसका उपयोग थोड़ा मुश्किल हो जाता है , इसलिए मैंने एक लाइनर को नीचे के कुछ छोटे पाचन योग्य काटने में तोड़ दिया है।

इस फ़ंक्शन का सार reduceप्रारंभिक मूल्य के साथ शुरू करने का उपयोग करना है Promise.resolve([]), या एक खाली सरणी वाला एक वादा है।

इस वादे को तब reduceविधि के रूप में पारित किया जाएगा promise। यह क्रमिक रूप से प्रत्येक वादे का पीछा करने की कुंजी है। निष्पादित करने के लिए अगला वादा है funcऔर जब thenआग लगती है, तो परिणाम संक्षिप्त हो जाते हैं और फिर उस वादे को वापस किया जाता है, reduceअगले वादा समारोह के साथ चक्र को निष्पादित करता है।

एक बार सभी वादों को पूरा करने के बाद, दिए गए वादे में प्रत्येक वादे के सभी परिणामों की एक सरणी होगी।

ES6 उदाहरण (एक लाइनर)

/*
 * serial executes Promises sequentially.
 * @param {funcs} An array of funcs that return promises.
 * @example
 * const urls = ['/url1', '/url2', '/url3']
 * serial(urls.map(url => () => $.ajax(url)))
 *     .then(console.log.bind(console))
 */
const serial = funcs =>
    funcs.reduce((promise, func) =>
        promise.then(result => func().then(Array.prototype.concat.bind(result))), Promise.resolve([]))

ES6 उदाहरण (टूट गया)

// broken down to for easier understanding

const concat = list => Array.prototype.concat.bind(list)
const promiseConcat = f => x => f().then(concat(x))
const promiseReduce = (acc, x) => acc.then(promiseConcat(x))
/*
 * serial executes Promises sequentially.
 * @param {funcs} An array of funcs that return promises.
 * @example
 * const urls = ['/url1', '/url2', '/url3']
 * serial(urls.map(url => () => $.ajax(url)))
 *     .then(console.log.bind(console))
 */
const serial = funcs => funcs.reduce(promiseReduce, Promise.resolve([]))

उपयोग:

// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']

// next convert each item to a function that returns a promise
const funcs = urls.map(url => () => $.ajax(url))

// execute them serially
serial(funcs)
    .then(console.log.bind(console))

1
बहुत अच्छा, धन्यवाद, Array.prototype.concat.bind(result)वह हिस्सा है जो मुझे याद आ रहा था, मैन्युअल रूप से परिणामों पर जोर देना था जो काम किया था लेकिन कम शांत था
zavr

चूंकि हम सभी आधुनिक जेएस के बारे में हैं, मेरा मानना ​​है कि console.log.bind(console)आपके अंतिम उदाहरण में बयान अब आम तौर पर अनावश्यक है। इन दिनों आप बस पास हो सकते हैं console.log। उदाहरण के लिए। serial(funcs).then(console.log)। वर्तमान नोडज और क्रोम पर परीक्षण किया गया।
मोलमोबी

मेरे सिर को लपेटने के लिए यह थोड़ा कठिन था लेकिन कमी अनिवार्य रूप से यह सही कर रही है? Promise.resolve([]).then((x) => { const data = mockApi('/data/1'); return Promise.resolve(x.concat(data)) }).then((x) => { const data = mockApi('/data/2'); return Promise.resolve(x.concat(data)); });
danecando

@danecando, हाँ यह सही लगता है। आप रिटर्न में Promise.resolve भी छोड़ सकते हैं, लौटे किसी भी मान को स्वचालित रूप से हल किया जाएगा जब तक आप Promise.reject को कॉल नहीं करते।
जोएलनेट

@joelnet, danecando की टिप्पणी के जवाब में, मुझे लगता है कि निम्नलिखित अभिव्यक्ति में क्या कम करना चाहिए और अधिक सही व्यक्त करना चाहिए, क्या आप सहमत हैं? Promise.resolve([]).then(x => someApiCall('url1').then(r => x.concat(r))).then(x => someApiCall('url2').then(r => x.concat(r)))और इसके बाद
बफ़रफ्लो7676

37

ऐसा करने के लिए बस ES6 में:

function(files) {
  // Create a new empty promise (don't do that with real people ;)
  var sequence = Promise.resolve();

  // Loop over each file, and add on a promise to the
  // end of the 'sequence' promise.
  files.forEach(file => {

    // Chain one computation onto the sequence
    sequence = 
      sequence
        .then(() => performComputation(file))
        .then(result => doSomething(result)); 
        // Resolves for each file, one at a time.

  })

  // This will resolve after the entire chain is resolved
  return sequence;
}

1
लगता है कि यह अंडरस्कोर का उपयोग कर रहा है। files.forEachयदि फ़ाइल एक सरणी है, तो आप इसे सरल बना सकते हैं ।
गुस्तावो रॉड्रिक्स

2
खैर ... यह ES5 है। ES6 रास्ता होगा for (file of files) {...}
गुस्तावो रॉड्रिक्स

1
आप कहते हैं कि आपको Promise.resolve()वास्तविक जीवन में पहले से ही हल किए गए वादे को बनाने के लिए उपयोग नहीं करना चाहिए । क्यों नहीं? Promise.resolve()से साफ लगता है new Promise(success => success())
21

8
@canac क्षमा करें, यह केवल शब्दों पर एक नाटक के साथ एक मजाक था ("खाली वादे ..")। Promise.resolve();अपने कोड में निश्चित रूप से उपयोग करें।
श्रीधर गुप्ता

1
अच्छा समाधान, पालन करने में आसान। मैंने एक फंक्शन में मेरा एनकाउंटर नहीं किया, इसलिए return sequence;मैंने डालने के बजाय अंत में हल करने के लिएsequence.then(() => { do stuff });
जो कोयल

25

मानक Node.js वादे के लिए सरल उपयोग:

function sequence(tasks, fn) {
    return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve());
}

अपडेट करें

आइटम-वादा एनपीएम पैकेज का उपयोग करने के लिए तैयार है।


6
मुझे यह अधिक विस्तार से समझाया गया है।
त्यगु 7

मैंने नीचे स्पष्टीकरण के साथ इस उत्तर का एक रूपांतर प्रदान किया। धन्यवाद
Sarsaparilla

यह वही है जो मैं पूर्व-नोड 7 वातावरण में करता हूं, जिसमें एसक्यूएन / वेट तक पहुंच नहीं है। अच्छा और साफ।
जेएचएच

11

मुझे बहुत सारे अनुक्रमिक कार्यों को चलाना पड़ता है और इन उत्तरों का उपयोग एक फ़ंक्शन बनाने के लिए किया जाता है जो किसी भी अनुक्रमिक कार्य को संभालने का ध्यान रखेगा ...

function one_by_one(objects_array, iterator, callback) {
    var start_promise = objects_array.reduce(function (prom, object) {
        return prom.then(function () {
            return iterator(object);
        });
    }, Promise.resolve()); // initial
    if(callback){
        start_promise.then(callback);
    }else{
        return start_promise;
    }
}

फ़ंक्शन 2 तर्क + 1 वैकल्पिक लेता है। पहला तर्क वह सरणी है जिस पर हम काम करेंगे। दूसरा तर्क स्वयं कार्य है, एक फ़ंक्शन जो एक वादा लौटाता है, अगला कार्य केवल तभी शुरू किया जाएगा जब यह वादा हल होगा। तीसरे तर्क को चलाने के लिए एक कॉलबैक है जब सभी कार्य किए गए हैं। यदि कोई कॉलबैक पारित नहीं होता है, तो फ़ंक्शन इसे बनाया गया वादा वापस करता है ताकि हम अंत को संभाल सकें।

यहाँ उपयोग का एक उदाहरण है:

var filenames = ['1.jpg','2.jpg','3.jpg'];
var resize_task = function(filename){
    //return promise of async resizing with filename
};
one_by_one(filenames,resize_task );

आशा है कि यह किसी को कुछ समय बचाता है ...


अतुल्य समाधान, यह सबसे अच्छा एक है जिसे मैंने लगभग एक हफ्ते की तस्करी में पाया है .... यह बहुत अच्छी तरह से समझाया गया है, तार्किक आंतरिक नाम हैं, एक अच्छा उदाहरण (बेहतर हो सकता है), मैं इसके लिए सुरक्षित रूप से कॉल कर सकता हूं आवश्यकतानुसार समय, और इसमें कॉलबैक सेट करने का विकल्प शामिल है। बस नीस! (बस नाम बदलकर कुछ ऐसा कर दिया जो मुझे अधिक समझ में आता है) .... दूसरों के लिए तर्क ... आप अपने 'ऑब्जेक्ट_अरे' के रूप में 'Object.keys ( myObject )' का उपयोग करके किसी वस्तु को पुन: प्रसारित कर सकते हैं
डेविडटुबमन

आपके कमेंट के लिए धन्यवाद! मैं उस नाम का उपयोग नहीं कर रहा हूं, लेकिन मैं इसे और अधिक स्पष्ट / सरल बनाना चाहता था।
साल्केटर

5

निकस्ट समाधान जो मुझे पता लगाने में सक्षम था वह bluebirdवादों के साथ था । आप बस वह कर सकते हैं Promise.resolve(files).each(fs.readFileAsync);जो गारंटी देता है कि वादों को क्रम से हल किया जाता है।


1
इससे भी बेहतर Promise.each(filtes, fs.readFileAsync):। Btw, क्या आपको नहीं करना है .bind(fs)?
बर्गी

यहाँ कोई भी किसी सरणी और अनुक्रम के बीच के अंतर को नहीं समझता है, कि उत्तरार्द्ध का अर्थ असीमित / गतिशील आकार है।
महत्वपूर्ण-टी।

ध्यान दें कि जावास्क्रिप्ट में एर्र्स का सी शैली भाषाओं में निश्चित आकार के सरणियों से कोई लेना-देना नहीं है। वे सिर्फ संख्यात्मक कुंजी प्रबंधन के साथ ऑब्जेक्ट हैं, जिन पर कोई निर्धारित आकार या सीमा नहीं है ( विशेष रूप से उपयोग करते समय नहीं है new Array(int)। यह सब lengthकुंजी-मूल्य जोड़ी को निर्धारित करता है , जिससे प्रभावित होता है कि लंबाई-आधारित पुनरावृत्ति के दौरान कितने सूचकांकों का उपयोग किया जाता है। यह शून्य है) वास्तविक सरणी के अनुक्रमण या सूचकांक सीमा पर प्रभाव)
माइक 'पोमैक्स' कामेरमेंस

4

यह ऊपर एक और उत्तर की थोड़ी भिन्नता है। देशी वादों का उपयोग करना:

function inSequence(tasks) {
    return tasks.reduce((p, task) => p.then(task), Promise.resolve())
}

व्याख्या

यदि आपके पास ये कार्य हैं [t1, t2, t3], तो उपरोक्त के बराबर है Promise.resolve().then(t1).then(t2).then(t3)। यह कम करने का व्यवहार है।

कैसे इस्तेमाल करे

पहले आपको कार्यों की एक सूची बनाने की आवश्यकता है! एक कार्य एक ऐसा कार्य है जो बिना किसी तर्क के स्वीकार करता है। यदि आपको अपने कार्य के लिए तर्क पारित करने की आवश्यकता है, तो bindकार्य बनाने के लिए अन्य तरीकों का उपयोग करें । उदाहरण के लिए:

var tasks = files.map(file => processFile.bind(null, file))
inSequence(tasks).then(...)

4

मेरा पसंदीदा समाधान:

function processArray(arr, fn) {
    return arr.reduce(
        (p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
        Promise.resolve([])
    );
}

यह मौलिक रूप से यहां प्रकाशित अन्य से अलग नहीं है लेकिन:

  • श्रृंखला में आइटम पर फ़ंक्शन लागू करता है
  • परिणामों की एक सरणी को हल करता है
  • Async / प्रतीक्षा की आवश्यकता नहीं है (समर्थन अभी भी सीमित है, लगभग 2017)
  • तीर के कार्यों का उपयोग करता है; अच्छा और संक्षिप्त

उदाहरण का उपयोग:

const numbers = [0, 4, 20, 100];
const multiplyBy3 = (x) => new Promise(res => res(x * 3));

// Prints [ 0, 12, 60, 300 ]
processArray(numbers, multiplyBy3).then(console.log);

उचित वर्तमान क्रोम (v59) और NodeJS (v8.1.2) पर परीक्षण किया गया।


3

उपयोग करें Array.prototype.reduce, और अपने वादों को एक समारोह में लपेटना याद रखें अन्यथा वे पहले से ही चल रहे होंगे!

// array of Promise providers

const providers = [
  function(){
     return Promise.resolve(1);
  },
  function(){
     return Promise.resolve(2);
  },
  function(){
     return Promise.resolve(3);
  }
]


const inSeries = function(providers){

  const seed = Promise.resolve(null); 

  return providers.reduce(function(a,b){
      return a.then(b);
  }, seed);
};

अच्छा और आसान ... आपको प्रदर्शन आदि के लिए उसी बीज को फिर से उपयोग करने में सक्षम होना चाहिए।

कम उपयोग करते समय केवल 1 तत्व के साथ खाली सरणियों या सरणियों के खिलाफ गार्ड करना महत्वपूर्ण है , इसलिए यह तकनीक आपका सबसे अच्छा दांव है:

   const providers = [
      function(v){
         return Promise.resolve(v+1);
      },
      function(v){
         return Promise.resolve(v+2);
      },
      function(v){
         return Promise.resolve(v+3);
      }
    ]

    const inSeries = function(providers, initialVal){

        if(providers.length < 1){
            return Promise.resolve(null)
        }

        return providers.reduce((a,b) => a.then(b), providers.shift()(initialVal));
    };

और फिर इसे कॉल करें:

inSeries(providers, 1).then(v => {
   console.log(v);  // 7
});

2

मैंने यह सरल तरीका वादा वस्तु पर बनाया है:

प्रोमिस ऑब्जेक्ट के लिए एक प्रोमिस। परिणाम विधि बनाएं और जोड़ें

Promise.sequence = function (chain) {
    var results = [];
    var entries = chain;
    if (entries.entries) entries = entries.entries();
    return new Promise(function (yes, no) {
        var next = function () {
            var entry = entries.next();
            if(entry.done) yes(results);
            else {
                results.push(entry.value[1]().then(next, function() { no(results); } ));
            }
        };
        next();
    });
};

उपयोग:

var todo = [];

todo.push(firstPromise);
if (someCriterium) todo.push(optionalPromise);
todo.push(lastPromise);

// Invoking them
Promise.sequence(todo)
    .then(function(results) {}, function(results) {});

वादा वस्तु के लिए इस विस्तार की सबसे अच्छी बात यह है कि यह वादों की शैली के अनुरूप है। Promise.all और Promise.fterence को एक ही तरीके से लागू किया जाता है, लेकिन अलग-अलग शब्दार्थ होते हैं।

सावधान

वादों का अनुक्रमिक चलना आमतौर पर वादों का उपयोग करने का एक बहुत अच्छा तरीका नहीं है। आमतौर पर Promise.all का उपयोग करना बेहतर है, और ब्राउज़र को जितनी जल्दी हो सके कोड चलाने दें। हालांकि, इसके लिए वास्तविक उपयोग के मामले हैं - उदाहरण के लिए, जावास्क्रिप्ट का उपयोग करते हुए मोबाइल ऐप लिखते समय।


नहीं, आप तुलना नहीं कर सकते हैं Promise.allऔर आपकी Promise.sequence। एक वादों का पुनरावृत्ति लेता है, दूसरा उन कार्यों की एक सरणी लेता है जो वादे वापस करते हैं।
बर्गी

Btw, मैं वादा
Bergi

नहीं पता था कि यह एक पुनरावृत्ति लिया। हालांकि इसे फिर से लिखना काफी आसान होना चाहिए। क्या आप इस बात का विस्तार कर सकते हैं कि यह वादा निर्माता एंटीपैटर्न क्यों है? मैंने आपकी पोस्ट यहाँ पढ़ी: stackoverflow.com/a/25569299/1667011
frodeborli

@Bergi मैंने पुनरावृत्तियों का समर्थन करने के लिए कोड अपडेट किया है। मैं अभी भी नहीं देखता कि यह एक एंटीपैटर्न है। आमतौर पर एंटीपार्टर्न को कोडिंग गलतियों से बचने के लिए दिशानिर्देश माना जाता है, और यह (लाइब्रेरी) फ़ंक्शन बनाने के लिए पूरी तरह से वैध है जो उन दिशानिर्देशों को तोड़ते हैं।
फ्रोडेबोरली

हाँ, यदि आप इसे एक पुस्तकालय कार्य मानते हैं तो यह ठीक है, लेकिन फिर भी इस मामले reduceमें बेंजामिन के जवाब में एक प्रश्न बहुत सरल है।
बरगी

2

आप इस फ़ंक्शन का उपयोग कर सकते हैं जिसे वादे मिलते हैं सूची:

function executeSequentially(promiseFactories) {
    var result = Promise.resolve();
    promiseFactories.forEach(function (promiseFactory) {
        result = result.then(promiseFactory);
    });
    return result;
}

वादा फैक्टरी सिर्फ एक सरल कार्य है जो एक वादा लौटाता है:

function myPromiseFactory() {
    return somethingThatCreatesAPromise();
}

यह काम करता है क्योंकि एक वादा कारखाने जब तक यह नहीं कहा जाता है वादा नहीं करता है। यह एक तत्कालीन कार्य के समान काम करता है - वास्तव में, यह एक ही बात है!

आप वादों की एक सरणी से अधिक काम नहीं करना चाहते हैं। प्रति वचन के अनुसार, जैसे ही कोई वादा किया जाता है, वह क्रियान्वित होने लगता है। तो क्या तुम सच में चाहते हैं वादा कारखानों की एक सरणी है ...

यदि आप वादों पर अधिक जानकारी चाहते हैं, तो आपको इस लिंक की जाँच करनी चाहिए: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html


2

Https://stackoverflow.com/a/31070150/7542429 पर आधारित मेरा उत्तर ।

Promise.series = function series(arrayOfPromises) {
    var results = [];
    return arrayOfPromises.reduce(function(seriesPromise, promise) {
      return seriesPromise.then(function() {
        return promise
        .then(function(result) {
          results.push(result);
        });
      });
    }, Promise.resolve())
    .then(function() {
      return results;
    });
  };

यह समाधान Promise.all () की तरह एक परिणाम के रूप में परिणाम देता है।

उपयोग:

Promise.series([array of promises])
.then(function(results) { 
  // do stuff with results here
});

2

मुझे वास्तव में @ joelnet का उत्तर पसंद आया, लेकिन मेरे लिए कोडिंग की शैली पचाने में थोड़ी कठिन है, इसलिए मैंने कुछ दिनों तक यह जानने की कोशिश की कि मैं एक ही समाधान को और अधिक पठनीय तरीके से कैसे व्यक्त करूंगा बस, एक अलग वाक्यविन्यास और कुछ टिप्पणियों के साथ।

// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']

// next convert each item to a function that returns a promise
const functions = urls.map((url) => {
  // For every url we return a new function
  return () => {
    return new Promise((resolve) => {
      // random wait in milliseconds
      const randomWait = parseInt((Math.random() * 1000),10)
      console.log('waiting to resolve in ms', randomWait)
      setTimeout(()=>resolve({randomWait, url}),randomWait)
    })
  }
})


const promiseReduce = (acc, next) => {
  // we wait for the accumulator to resolve it's promise
  return acc.then((accResult) => {
    // and then we return a new promise that will become
    // the new value for the accumulator
    return next().then((nextResult) => {
      // that eventually will resolve to a new array containing
      // the value of the two promises
      return accResult.concat(nextResult)
    })
  })
};
// the accumulator will always be a promise that resolves to an array
const accumulator = Promise.resolve([])

// we call reduce with the reduce function and the accumulator initial value
functions.reduce(promiseReduce, accumulator)
  .then((result) => {
    // let's display the final value here
    console.log('=== The final result ===')
    console.log(result)
  })

2

जैसा कि बर्गी ने देखा, मुझे लगता है कि सबसे अच्छा और स्पष्ट समाधान ब्लूबर्ड.चेक, नीचे दिए गए कोड का उपयोग है:

const BlueBird = require('bluebird');
BlueBird.each(files, fs.readFileAsync);

2

सबसे पहले, आपको यह समझने की आवश्यकता है कि एक वादा निर्माण के समय निष्पादित किया जाता है।
उदाहरण के लिए यदि आपके पास एक कोड है:

["a","b","c"].map(x => returnsPromise(x))

आपको इसे बदलने की आवश्यकता है:

["a","b","c"].map(x => () => returnsPromise(x))

फिर हमें क्रमिक रूप से श्रृंखला वादों की आवश्यकता है:

["a", "b", "c"].map(x => () => returnsPromise(x))
    .reduce(
        (before, after) => before.then(_ => after()),
        Promise.resolve()
    )

निष्पादित after(), यह सुनिश्चित करेगा कि वादा किया जाता है (और निष्पादित) केवल जब इसका समय आता है।


1

प्रॉमिस ऑब्जेक्ट का विस्तार करने के लिए मैं निम्नलिखित कोड का उपयोग करता हूं। यह वादों की अस्वीकृति को संभालता है और परिणामों की एक सरणी लौटाता है

कोड

/*
    Runs tasks in sequence and resolves a promise upon finish

    tasks: an array of functions that return a promise upon call.
    parameters: an array of arrays corresponding to the parameters to be passed on each function call.
    context: Object to use as context to call each function. (The 'this' keyword that may be used inside the function definition)
*/
Promise.sequence = function(tasks, parameters = [], context = null) {
    return new Promise((resolve, reject)=>{

        var nextTask = tasks.splice(0,1)[0].apply(context, parameters[0]); //Dequeue and call the first task
        var output = new Array(tasks.length + 1);
        var errorFlag = false;

        tasks.forEach((task, index) => {
            nextTask = nextTask.then(r => {
                output[index] = r;
                return task.apply(context, parameters[index+1]);
            }, e=>{
                output[index] = e;
                errorFlag = true;
                return task.apply(context, parameters[index+1]);
            });
        });

        // Last task
        nextTask.then(r=>{
            output[output.length - 1] = r;
            if (errorFlag) reject(output); else resolve(output);
        })
        .catch(e=>{
            output[output.length - 1] = e;
            reject(output);
        });
    });
};

उदाहरण

function functionThatReturnsAPromise(n) {
    return new Promise((resolve, reject)=>{
        //Emulating real life delays, like a web request
        setTimeout(()=>{
            resolve(n);
        }, 1000);
    });
}

var arrayOfArguments = [['a'],['b'],['c'],['d']];
var arrayOfFunctions = (new Array(4)).fill(functionThatReturnsAPromise);


Promise.sequence(arrayOfFunctions, arrayOfArguments)
.then(console.log)
.catch(console.error);

1

यदि आप चाहते हैं कि आप एक अनुक्रमिक वादा करने के लिए कम कर सकते हैं, उदाहरण के लिए:

[2,3,4,5,6,7,8,9].reduce((promises, page) => {
    return promises.then((page) => {
        console.log(page);
        return Promise.resolve(page+1);
    });
  }, Promise.resolve(1));

यह हमेशा अनुक्रमिक में काम करेगा।


1

आधुनिक ES का उपयोग करना:

const series = async (tasks) => {
  const results = [];

  for (const task of tasks) {
    const result = await task;

    results.push(result);
  }

  return results;
};

//...

const readFiles = await series(files.map(readFile));

1

Async / Await के साथ (यदि आपके पास ES7 का समर्थन है)

function downloadFile(fileUrl) { ... } // This function return a Promise

async function main()
{
  var filesList = [...];

  for (const file of filesList) {
    await downloadFile(file);
  }
}

(आपको forलूप का उपयोग करना चाहिए , और forEachइसलिए नहीं कि async / प्रतीक्षा में forEach लूप में चल रही समस्याएं हैं)

Async / Await के बिना (वादा का उपयोग करके)

function downloadFile(fileUrl) { ... } // This function return a Promise

function downloadRecursion(filesList, index)
{
  index = index || 0;
  if (index < filesList.length)
  {
    downloadFile(filesList[index]).then(function()
    {
      index++;
      downloadRecursion(filesList, index); // self invocation - recursion!
    });
  }
  else
  {
    return Promise.resolve();
  }
}

function main()
{
  var filesList = [...];
  downloadRecursion(filesList);
}

2
फॉरचच के अंदर इंतजार की सिफारिश नहीं की गई है।
मार्सेलो एग्मोवेल

@ MarceloAgimóvel - मैं के साथ काम नहीं करने के लिए समाधान के लिए अद्यतन किया है forEach(के अनुसार इस )
गिल Epshtain

0

प्रश्न के शीर्षक के आधार पर, "एक के बाद एक वादों को हल करें (यानी अनुक्रम में?"), हम यह समझ सकते हैं कि ओपी प्रति सेकंड अनुक्रमिक कॉल की तुलना में निपटान पर वादों के अनुक्रमिक हैंडलिंग में अधिक रुचि रखता है।

यह उत्तर दिया जाता है:

  • यह प्रदर्शित करने के लिए कि अनुक्रियाओं के अनुक्रमिक संचालन के लिए अनुक्रमिक कॉल आवश्यक नहीं हैं।
  • इस पृष्ठ के आगंतुकों के लिए व्यवहार्य वैकल्पिक पैटर्न को उजागर करने के लिए - ओपी सहित अगर वह अभी भी एक साल बाद भी रुचि रखता है।
  • ओपी के इस दावे के बावजूद कि वह कॉल को समवर्ती नहीं बनाना चाहते हैं, जो वास्तव में मामला हो सकता है लेकिन समान रूप से प्रतिक्रियाओं की अनुक्रमिक हैंडलिंग की इच्छा के आधार पर एक धारणा हो सकती है जैसा कि शीर्षक का अर्थ है।

यदि समवर्ती कॉल वास्तव में नहीं चाहते हैं तो बेंजामिन ग्रुएनबाम का उत्तर देखें, जो अनुक्रमिक कॉल (आदि) को व्यापक रूप से कवर करता है।

हालांकि, आप उन प्रतिमानों (बेहतर प्रदर्शन के लिए) में रुचि रखते हैं जो समवर्ती कॉल के बाद अनुक्रियाओं के अनुक्रमिक संचालन की अनुमति देते हैं, तो कृपया इसे पढ़ें।

यह सोचने के लिए आपको लुभावना है कि आपको उपयोग करना होगा Promise.all(arr.map(fn)).then(fn)(जैसा कि मैंने कई बार किया है) या प्रोमिस लिबास की फैंसी चीनी (विशेषकर ब्लूबर्ड्स), हालांकि, इस लेख के क्रेडिट के साथ एक arr.map(fn).reduce(fn)पैटर्न काम करेगा, इसके फायदे इसके साथ हैं:

  • किसी भी वादे के साथ काम करता है - यहां तक ​​कि jQuery के पूर्व-अनुरूप संस्करणों - केवल .then() उपयोग किया जाता है।
  • लचीलेपन को स्किप-ओवर-एरर या स्टॉप-ऑन-एरर में सुधारता है, जो भी आप एक पंक्ति मॉड के साथ चाहते हैं।

यहाँ यह है, के लिए लिखा है Q

var readFiles = function(files) {
    return files.map(readFile) //Make calls in parallel.
    .reduce(function(sequence, filePromise) {
        return sequence.then(function() {
            return filePromise;
        }).then(function(file) {
            //Do stuff with file ... in the correct sequence!
        }, function(error) {
            console.log(error); //optional
            return sequence;//skip-over-error. To stop-on-error, `return error` (jQuery), or `throw  error` (Promises/A+).
        });
    }, Q()).then(function() {
        // all done.
    });
};

नोट: केवल वही एक टुकड़ा, जो Q()Q के लिए विशिष्ट है। jQuery के लिए आपको यह सुनिश्चित करने की आवश्यकता है कि readFile () एक jQuery वादा लौटाता है। A + lib के साथ, विदेशी वादों को आत्मसात किया जाएगा।

यहां कुंजी कटौती का sequenceवादा है, जो वादों से निपटने का अनुक्रम बनाती है, readFileलेकिन उनके निर्माण का नहीं।

और एक बार जब आप इसे अवशोषित कर लेते हैं, तो शायद यह थोड़ा दिमाग उड़ाने वाला होता है जब आपको पता चलता है कि .map()मंच वास्तव में आवश्यक नहीं है! संपूर्ण कार्य, समानांतर कॉल प्लस सीरियल हैंडलिंग सही क्रम में, reduce()अकेले के साथ प्राप्त किया जा सकता है , और इसके अलावा लचीलेपन का अतिरिक्त लाभ:

  • समानांतर async कॉल से धारावाहिक async कॉल में परिवर्तित करके केवल एक पंक्ति को स्थानांतरित करना - संभवतः विकास के दौरान उपयोगी।

यहाँ यह है, Qफिर से के लिए।

var readFiles = function(files) {
    return files.reduce(function(sequence, f) {
        var filePromise = readFile(f);//Make calls in parallel. To call sequentially, move this line down one.
        return sequence.then(function() {
            return filePromise;
        }).then(function(file) {
            //Do stuff with file ... in the correct sequence!
        }, function(error) {
            console.log(error); //optional
            return sequence;//Skip over any errors. To stop-on-error, `return error` (jQuery), or `throw  error` (Promises/A+).
        });
    }, Q()).then(function() {
        // all done.
    });
};

यही मूल पैटर्न है। यदि आप कॉल करने वाले को भी डेटा वितरित करना चाहते हैं (जैसे फाइलें या उनमें से कुछ परिवर्तन), तो आपको एक हल्के संस्करण की आवश्यकता होगी।


मुझे नहीं लगता कि ओपी के इरादों के विपरीत सवालों के जवाब देने के लिए यह एक अच्छा विचार है ...
बर्गी

1
यह sequence.then(() => filePromise)चीज एक एंटीपैटर्न है - यह जितनी जल्दी हो सके त्रुटियों को प्रचारित नहीं करता है (और unhandledRejectionउन्हें समर्थन देने वाले कार्यों में बनाता है)। आप बल्कि का उपयोग करना चाहिए Q.all([sequence, filePromise])या $.when(sequence, filePromise)। बेशक, यह व्यवहार वह हो सकता है जिसे आप तब चाहते हैं जब आप त्रुटियों को अनदेखा करना या छोड़ना चाहते हैं, लेकिन आपको कम से कम इसका नुकसान के रूप में उल्लेख करना चाहिए।
बरगी

@Bergi, मुझे उम्मीद है कि ओपी इसमें कदम रखेगा और इस पर निर्णय देगा कि क्या यह वास्तव में उसके इरादों के विपरीत है या नहीं। यदि नहीं, तो मुझे लगता है कि मैं उत्तर को हटा दूंगा, इस बीच मुझे आशा है कि मैंने अपनी स्थिति को सही ठहराया है। सभ्य प्रतिक्रिया प्रदान करने के लिए इसे गंभीरता से लेने के लिए धन्यवाद। क्या आप एंटी-पैटर्न के बारे में अधिक बता सकते हैं, या कृपया एक संदर्भ प्रदान करें? क्या यह उस लेख पर लागू होता है जहाँ मुझे मूल पैटर्न मिला था ?
रोमेर -1888

1
हां, उनके कोड का तीसरा संस्करण (जो "समानांतर और अनुक्रमिक दोनों" है) में एक ही समस्या है। "एंटीपैटर" को परिष्कृत त्रुटि हैंडलिंग की आवश्यकता होती है और हैंडलर को अतुल्यकालिक रूप से संलग्न करने के लिए प्रवण होता है, जो unhandledRejectionघटनाओं का कारण बनता है। ब्लूबर्ड में आप इसके आसपास काम कर सकते हैं sequence.return(filePromise)जिसमें समान व्यवहार होता है लेकिन अस्वीकृति को ठीक करता है। मुझे कोई संदर्भ नहीं पता है, मैं अभी इसके साथ आया हूं - मुझे नहीं लगता कि "(एंटी) पैटर्न" का अभी तक कोई नाम है।
बरगी

1
@ बर्गी, आप स्पष्ट रूप से कुछ देख सकते हैं जो मैं नहीं कर सकता :( मुझे आश्चर्य है कि अगर इस नए विरोधी पैटर्न को कहीं दस्तावेज करने की आवश्यकता है?
Roamer-1888

0

आपका दृष्टिकोण बुरा नहीं है, लेकिन इसमें दो मुद्दे हैं: यह त्रुटियों को निगलता है और यह स्पष्ट वादा निर्माण एंटीपैटर्न को नियोजित करता है।

आप इन दोनों मुद्दों को हल कर सकते हैं, और कोड को क्लीनर बना सकते हैं, जबकि अभी भी एक ही सामान्य रणनीति को नियोजित कर सकते हैं:

var Q = require("q");

var readFile = function(file) {
  ... // Returns a promise.
};

var readFiles = function(files) {
  var readSequential = function(index) {
    if (index < files.length) {
      return readFile(files[index]).then(function() {
        return readSequential(index + 1);
      });
    }
  };

  // using Promise.resolve() here in case files.length is 0
  return Promise.resolve(readSequential(0)); // Start!
};

0

अगर किसी और को CRUD ऑपरेशन करते समय वादों को हल करने के STRICTLY अनुक्रमिक तरीके के एक गारंटीकृत तरीके की आवश्यकता है, तो आप निम्न कोड को आधार के रूप में भी उपयोग कर सकते हैं।

जब तक आप प्रत्येक फ़ंक्शन को कॉल करने से पहले 'रिटर्न' जोड़ते हैं, एक वादा का वर्णन करते हैं, और इस उदाहरण का उपयोग अगले आधार के रूप में करते हैं। तब () फ़ंक्शन कॉल पिछले एक के पूरा होने के बाद शुरू होगा:

getRidOfOlderShoutsPromise = () => {
    return readShoutsPromise('BEFORE')
    .then(() => {
        return deleteOlderShoutsPromise();
    })
    .then(() => {
        return readShoutsPromise('AFTER')
    })
    .catch(err => console.log(err.message));
}

deleteOlderShoutsPromise = () => {
    return new Promise ( (resolve, reject) => {
        console.log("in deleteOlderShouts");
        let d = new Date();
        let TwoMinuteAgo = d - 1000 * 90 ;
        All_Shouts.deleteMany({ dateTime: {$lt: TwoMinuteAgo}}, function(err) {
            if (err) reject();
            console.log("DELETED OLDs at "+d);
            resolve();        
        });
    });
}

readShoutsPromise = (tex) => {
    return new Promise( (resolve, reject) => {
        console.log("in readShoutsPromise -"+tex);
        All_Shouts
        .find({})
        .sort([['dateTime', 'ascending']])
        .exec(function (err, data){
            if (err) reject();
            let d = new Date();
            console.log("shouts "+tex+" delete PROMISE = "+data.length +"; date ="+d);
            resolve(data);
        });    
    });
}

0

वादों के अनुक्रम के लिए ऐरे पुश और पॉप विधि का उपयोग किया जा सकता है। अतिरिक्त डेटा की आवश्यकता होने पर आप नए वादों को भी आगे बढ़ा सकते हैं। यह कोड है, मैं पेजों के अनुक्रम को लोड करने के लिए React Infinite लोडर में उपयोग करूंगा।

var promises = [Promise.resolve()];

function methodThatReturnsAPromise(page) {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			console.log(`Resolve-${page}! ${new Date()} `);
			resolve();
		}, 1000);
	});
}

function pushPromise(page) {
	promises.push(promises.pop().then(function () {
		return methodThatReturnsAPromise(page)
	}));
}

pushPromise(1);
pushPromise(2);
pushPromise(3);


0

अधिकांश उत्तरों में व्यक्तिगत रूप से सभी वादों के परिणाम शामिल नहीं होते हैं, इसलिए यदि कोई व्यक्ति इस विशेष व्यवहार की तलाश कर रहा है, तो यह एक संभावित समाधान है जिसका उपयोग प्रतिरूपण का उपयोग करके किया जाता है।

यह इस प्रकार है Promise.all:

  • .then()कॉलबैक में परिणामों की सरणी देता है ।

  • यदि कुछ वादा विफल हो जाता है, तो .catch()कॉलबैक में तुरंत वापस आ जाता है ।

const promiseEach = (arrayOfTasks) => {
  let results = []
  return new Promise((resolve, reject) => {
    const resolveNext = (arrayOfTasks) => {
      // If all tasks are already resolved, return the final array of results
      if (arrayOfTasks.length === 0) return resolve(results)

      // Extract first promise and solve it
      const first = arrayOfTasks.shift()

      first().then((res) => {
        results.push(res)
        resolveNext(arrayOfTasks)
      }).catch((err) => {
        reject(err)
      })
    }
    resolveNext(arrayOfTasks)
  })
}

// Lets try it 😎

const promise = (time, shouldThrowError) => new Promise((resolve, reject) => {
  const timeInMs = time * 1000
  setTimeout(()=>{
    console.log(`Waited ${time} secs`)
    if (shouldThrowError) reject(new Error('Promise failed'))
    resolve(time)
  }, timeInMs)
})

const tasks = [() => promise(1), () => promise(2)]

promiseEach(tasks)
  .then((res) => {
    console.log(res) // [1, 2]
  })
  // Oops some promise failed
  .catch((error) => {
    console.log(error)
  })

के बारे में ध्यान दें tasksसरणी घोषणा के :

इस मामले में निम्नलिखित संकेतन का उपयोग करना संभव नहीं है जैसे Promise.allकि उपयोग करेगा:

const tasks = [promise(1), promise(2)]

और हमें उपयोग करना होगा:

const tasks = [() => promise(1), () => promise(2)]

कारण यह है कि जावास्क्रिप्ट ने घोषणा के तुरंत बाद वादे को क्रियान्वित करना शुरू कर दिया। यदि हम इस तरह के तरीकों का उपयोग करते हैं Promise.all, तो यह जांचता है कि उन सभी की स्थिति fulfilledया है rejected, लेकिन खुद को शुरू नहीं करता है। उपयोग करने से () => promise()हम निष्पादन को तब तक रोकते हैं जब तक कि उसे बुलाया न जाए।


0
(function() {
  function sleep(ms) {
    return new Promise(function(resolve) {
      setTimeout(function() {
        return resolve();
      }, ms);
    });
  }

  function serial(arr, index, results) {
    if (index == arr.length) {
      return Promise.resolve(results);
    }
    return new Promise(function(resolve, reject) {
      if (!index) {
        index = 0;
        results = [];
      }
      return arr[index]()
        .then(function(d) {
          return resolve(d);
        })
        .catch(function(err) {
          return reject(err);
        });
    })
      .then(function(result) {
        console.log("here");
        results.push(result);
        return serial(arr, index + 1, results);
      })
      .catch(function(err) {
        throw err;
      });
  }

  const a = [5000, 5000, 5000];

  serial(a.map(x => () => sleep(x)));
})();

यहाँ कुंजी है कि आप स्लीप फ़ंक्शन को कैसे कहते हैं। आपको कई प्रकार के कार्यों को पारित करने की आवश्यकता होती है, जो वादों की एक सरणी के बजाय एक वादा वापस करता है।


-1

यह स्पैक्स के परिणाम के आधार पर गतिशील / अनंत अनुक्रमों का समर्थन करते हुए अधिक सामान्य तरीके से वादों के अनुक्रम को कैसे संसाधित किया जाए, इसका विस्तार करना है:

var $q = require("q");
var spex = require('spex')($q);

var files = []; // any dynamic source of files;

var readFile = function (file) {
    // returns a promise;
};

function source(index) {
    if (index < files.length) {
        return readFile(files[index]);
    }
}

function dest(index, data) {
    // data = resolved data from readFile;
}

spex.sequence(source, dest)
    .then(function (data) {
        // finished the sequence;
    })
    .catch(function (error) {
        // error;
    });

न केवल यह समाधान किसी भी आकार के दृश्यों के साथ काम करेगा, बल्कि आप इसमें आसानी से डेटा थ्रॉटलिंग और लोड संतुलन जोड़ सकते हैं ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.