वादे के लिए लूप लिखने का सही तरीका।


116

निम्न वादा कॉल और जंजीर logger.log (Res) सुनिश्चित करने के लिए लूप का सही ढंग से निर्माण कैसे करें ? (Bluebird)

db.getUser(email).then(function(res) { logger.log(res); }); // this is a promise

मैंने निम्न तरीके की कोशिश की ( http://blog.victorquinn.com/javascript-promise-ORE-loop से विधि )

var Promise = require('bluebird');

var promiseWhile = function(condition, action) {
    var resolver = Promise.defer();

    var loop = function() {
        if (!condition()) return resolver.resolve();
        return Promise.cast(action())
            .then(loop)
            .catch(resolver.reject);
    };

    process.nextTick(loop);

    return resolver.promise;
});

var count = 0;
promiseWhile(function() {
    return count < 10;
}, function() {
    return new Promise(function(resolve, reject) {
        db.getUser(email)
          .then(function(res) { 
              logger.log(res); 
              count++;
              resolve();
          });
    }); 
}).then(function() {
    console.log('all done');
}); 

हालांकि यह काम करने लगता है, लेकिन मुझे नहीं लगता कि यह कॉल करने के क्रम की गारंटी देता है loglog.log (res);

कोई सुझाव?


1
कोड मुझे ठीक लगता है ( loopफ़ंक्शन के साथ पुनर्संरचना तुल्यकालिक छोरों को करने का तरीका है)। आपको क्या लगता है कि कोई गारंटी नहीं है?
ह्यूगोमग

db.getUser (ईमेल) को क्रम में बुलाने की गारंटी है। लेकिन, चूंकि db.getUser () अपने आप में एक वादा है, इसे क्रमिक रूप से कॉल करने का अर्थ यह नहीं है कि 'ईमेल' के लिए डेटाबेस क्वेरीज़ वादे की एसिंक्रोनस सुविधा के कारण क्रमिक रूप से चलती हैं। इस प्रकार, logger.log (res) को इस पर निर्भर किया जाता है कि किस क्वेरी को पूरा करने के लिए होता है।
user2127480

1
@ user2127480: लेकिन लूप के अगले पुनरावृत्ति को क्रमिक रूप से कहा जाता है केवल वादे को हल करने के बाद, यह है कि whileकोड कैसे काम करता है?
बर्गी जूल

जवाबों:


78

मुझे नहीं लगता कि यह लॉगर.लॉग (रेस) को बुलाने के आदेश की गारंटी देता है;

असल में, यह करता है। resolveकॉल से पहले उस स्टेटमेंट को निष्पादित किया जाता है ।

कोई सुझाव?

बहुत सारे। सबसे महत्वपूर्ण आपके द्वारा किए गए वादे-प्रति-मैन्युअल एंटीपैटर्न का उपयोग है - केवल करना

promiseWhile(…, function() {
    return db.getUser(email)
             .then(function(res) { 
                 logger.log(res); 
                 count++;
             });
})…

दूसरा, उस whileकार्य को बहुत सरल बनाया जा सकता है:

var promiseWhile = Promise.method(function(condition, action) {
    if (!condition()) return;
    return action().then(promiseWhile.bind(null, condition, action));
});

तीसरा, मैं whileलूप का उपयोग नहीं करूंगा (क्लोजर वैरिएबल के साथ) लेकिन forलूप:

var promiseFor = Promise.method(function(condition, action, value) {
    if (!condition(value)) return value;
    return action(value).then(promiseFor.bind(null, condition, action));
});

promiseFor(function(count) {
    return count < 10;
}, function(count) {
    return db.getUser(email)
             .then(function(res) { 
                 logger.log(res); 
                 return ++count;
             });
}, 0).then(console.log.bind(console, 'all done'));

2
उफ़। सिवाय इसके कि इसके तर्क के रूप में actionलेता valueहै promiseFor। एसओ मुझे ऐसा छोटा संपादन नहीं करने देगा। धन्यवाद, यह बहुत उपयोगी और सुरुचिपूर्ण है।
गॉर्डन

1
@ Roamer-1888: हो सकता है कि शब्दावली थोड़ी अजीब हो, लेकिन मेरा मतलब है कि एक whileलूप कुछ वैश्विक स्थिति का परीक्षण करता है, जबकि एक forलूप का लूप बॉडी से ही होता है। वास्तव में मैंने अधिक कार्यात्मक दृष्टिकोण का उपयोग किया है जो लूप की तुलना में फ़िक्सपॉइंट पुनरावृत्ति की तरह दिखता है। उनके कोड को फिर से जांचें, valueपैरामीटर अलग है।
बरगी २

2
ठीक है, मैं इसे अब देखता हूं। जैसा कि .bind()नए valueको रोकता है, मुझे लगता है कि मैं पठनीयता के लिए फ़ंक्शन को लंबा करना चुन सकता हूं। और खेद है कि अगर मैं मोटा हो रहा हूं, लेकिन अगर promiseForऔर promiseWhileसह-अस्तित्व नहीं है, तो कोई दूसरे को कैसे बुलाएगा?
रोमेर -1888

2
@herve आप मूल रूप से इसे छोड़ सकते हैं और इसके return …द्वारा प्रतिस्थापित कर सकते हैं return Promise.resolve(…)। यदि आपको किसी अपवाद के विरुद्ध conditionया उसे actionफेंकने के लिए अतिरिक्त सुरक्षा उपायों की आवश्यकता होती है (जैसे कि Promise.methodयह प्रदान करता है ), तो पूरे फ़ंक्शन बॉडी को एक में लपेटेंreturn Promise.resolve().then(() => { … })
Bergi

2
@ हेरवे वास्तव में ऐसा होना चाहिए Promise.resolve().then(action).…या Promise.resolve(action()).…, आपको then
बरगी

134

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

जहाँ तक मैं बता सकता हूँ कि आप कोशिश कर रहे हैं:

  • ईमेल पते के संग्रह के लिए उपयोगकर्ता विवरणों की एक श्रृंखला को अतुल्यकालिक रूप से प्राप्त करना (कम से कम, यह एकमात्र ऐसा परिदृश्य है जो समझ में आता है)।
  • .then()पुनरावृत्ति के माध्यम से एक श्रृंखला का निर्माण करके ऐसा करना ।
  • दिए गए परिणामों को संभालने के दौरान मूल क्रम बनाए रखने के लिए।

इस प्रकार परिभाषित, समस्या वास्तव में प्रोमिस एंटी-पैटर्न में "द कलेक्शन केर्फफल" के तहत चर्चा की गई है , जो सरल समाधान प्रदान करती है:

  • समानांतर अतुल्यकालिक कॉल का उपयोग कर Array.prototype.map()
  • धारावाहिक अतुल्यकालिक कॉल का उपयोग कर Array.prototype.reduce()

समानांतर दृष्टिकोण (सीधा) उस मुद्दे को देगा जिसे आप बचने की कोशिश कर रहे हैं - कि प्रतिक्रियाओं का क्रम अनिश्चित है। धारावाहिक दृष्टिकोण आवश्यक .then()श्रृंखला का निर्माण करेगा - फ्लैट - कोई पुनरावृत्ति नहीं।

function fetchUserDetails(arr) {
    return arr.reduce(function(promise, email) {
        return promise.then(function() {
            return db.getUser(email).done(function(res) {
                logger.log(res);
            });
        });
    }, Promise.resolve());
}

कॉल इस प्रकार है:

//Compose here, by whatever means, an array of email addresses.
var arrayOfEmailAddys = [...];

fetchUserDetails(arrayOfEmailAddys).then(function() {
    console.log('all done');
});

जैसा कि आप देख सकते हैं, बदसूरत बाहरी संस्करण की कोई आवश्यकता नहीं है countया यह संबद्ध conditionफ़ंक्शन है। सीमा (प्रश्न में 10) पूरी तरह से सरणी की लंबाई से निर्धारित होती है arrayOfEmailAddys


16
ऐसा लगता है कि चयनित उत्तर होना चाहिए। सुंदर और बहुत पुन: प्रयोज्य दृष्टिकोण।
कीन

1
क्या किसी को पता है कि क्या कैच माता-पिता को वापस प्रचारित करेगा? उदाहरण के लिए यदि db.getUser विफल होते, तो क्या (अस्वीकार) त्रुटि वापस प्रचारित होती?
वायोफैफ्ट सीवन

@wayofthefuture, नहीं। इसे इस तरह से समझें ..... आप इतिहास को नहीं बदल सकते।
रोमेर -1888

4
जवाब के लिए धन्यवाद। यह स्वीकृत उत्तर होना चाहिए।
klvs

1
@ Roamer-1888 मेरी गलती, मैंने मूल प्रश्न को गलत बताया। मैं (व्यक्तिगत रूप से) एक समाधान में देख रहा था, जहां आपके अनुरोध को कम करने के लिए आवश्यक इंटलियल सूची बढ़ती जा रही है (यह एक DB की एक क्वेरी)। इस मामले में मुझे एक जनरेटर के साथ कम उपयोग करने का विचार मिला (1) वादा श्रृंखला के सशर्त विस्तार और (2) लौटाए गए रेज़ल की खपत।
jhp

40

यहां बताया गया है कि मैं इसे मानक वादा वस्तु के साथ कैसे करता हूं।

// Given async function sayHi
function sayHi() {
  return new Promise((resolve) => {
    setTimeout(() => {
      console.log('Hi');
      resolve();
    }, 3000);
  });
}

// And an array of async functions to loop through
const asyncArray = [sayHi, sayHi, sayHi];

// We create the start of a promise chain
let chain = Promise.resolve();

// And append each function in the array to the promise chain
for (const func of asyncArray) {
  chain = chain.then(func);
}

// Output:
// Hi
// Hi (After 3 seconds)
// Hi (After 3 more seconds)

शानदार उत्तर @youngwerth
जैम

3
इस तरह से परमेस कैसे भेजें?
आकाश खां

4
@ चेन पर चेन = श्रृंखला (फंक) लाइन, आप chain = chain.then(func.bind(null, "...your params here")); या तो कर सकते हैं: या chain = chain.then(() => func("your params here"));
युवावस्था

9

दिया हुआ

  • asyncFn फ़ंक्शन
  • मदों की सरणी

अपेक्षित

  • श्रृंखला में .then () का वादा करें (क्रम में)
  • देशी es6

उपाय

let asyncFn = (item) => {
  return new Promise((resolve, reject) => {
    setTimeout( () => {console.log(item); resolve(true)}, 1000 )
  })
}

// asyncFn('a')
// .then(()=>{return async('b')})
// .then(()=>{return async('c')})
// .then(()=>{return async('d')})

let a = ['a','b','c','d']

a.reduce((previous, current, index, array) => {
  return previous                                    // initiates the promise chain
  .then(()=>{return asyncFn(array[index])})      //adds .then() promise for each item
}, Promise.resolve())

2
यदि asyncयह जावास्क्रिप्ट में एक आरक्षित शब्द बनने वाला है तो यह उस फ़ंक्शन का नाम बदलने के लिए स्पष्टता जोड़ सकता है।
हिप्पिट्रैएल

इसके अलावा, क्या यह मामला नहीं है कि ब्रेसिज़ में शरीर के बिना वसा तीर कार्य करता है, जो कि वहां की अभिव्यक्ति का मूल्यांकन करता है? इससे कोड अधिक संक्षिप्त हो जाएगा। मैं एक टिप्पणी यह ​​भी कह सकता हूं कि currentअप्रयुक्त है।
हिप्पिट्रैएल

2
यह उचित तरीका है!
teleme.io

4

इसे हल करने का एक नया तरीका है और यह async / wait का उपयोग करके है।

async function myFunction() {
  while(/* my condition */) {
    const res = await db.getUser(email);
    logger.log(res);
  }
}

myFunction().then(() => {
  /* do other stuff */
})

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function https://ponyfoo.com/articles/understanding-javascript-async/await


धन्यवाद, इसमें एक रूपरेखा (ब्लूबर्ड) का उपयोग करना शामिल नहीं है।
रॉल्फ

3

बर्गी का सुझाया गया कार्य वास्तव में अच्छा है:

var promiseWhile = Promise.method(function(condition, action) {
      if (!condition()) return;
    return action().then(promiseWhile.bind(null, condition, action));
});

फिर भी मैं एक छोटा जोड़ बनाना चाहता हूं, जो वादों का उपयोग करते समय समझ में आता है:

var promiseWhile = Promise.method(function(condition, action, lastValue) {
  if (!condition()) return lastValue;
  return action().then(promiseWhile.bind(null, condition, action));
});

इस तरह जबकि लूप को एक वादा श्रृंखला में एम्बेड किया जा सकता है और अंतिमवैल्यू के साथ हल हो सकता है (भले ही कार्रवाई () कभी नहीं चलती है)। उदाहरण देखें:

var count = 10;
util.promiseWhile(
  function condition() {
    return count > 0;
  },
  function action() {
    return new Promise(function(resolve, reject) {
      count = count - 1;
      resolve(count)
    })
  },
  count)

3

मैं कुछ इस तरह से बनाऊंगा:

var request = []
while(count<10){
   request.push(db.getUser(email).then(function(res) { return res; }));
   count++
};

Promise.all(request).then((dataAll)=>{
  for (var i = 0; i < dataAll.length; i++) {

      logger.log(dataAll[i]); 
  }  
});

इस तरह, dataAll लॉग करने के लिए सभी तत्व का एक क्रमबद्ध सरणी है। और लॉग ऑपरेशन प्रदर्शन करेगा जब सभी वादे किए जाते हैं।


Promise.all एक ही समय में वादों को कॉल करेगा। तो पूरा होने का क्रम बदल सकता है। प्रश्न जंजीर वादों के लिए पूछता है। इसलिए पूरा होने का क्रम नहीं बदलना चाहिए।
कैनबैक्स

संपादन 1: आपको Promise.all पर कॉल करने की आवश्यकता नहीं है। जब तक वादों को पूरा नहीं किया जाएगा, तब तक वे समानांतर रूप से क्रियान्वित किए जाएंगे।
कैनबैक्स


0
function promiseLoop(promiseFunc, paramsGetter, conditionChecker, eachFunc, delay) {
    function callNext() {
        return promiseFunc.apply(null, paramsGetter())
            .then(eachFunc)
    }

    function loop(promise, fn) {
        if (delay) {
            return new Promise(function(resolve) {
                setTimeout(function() {
                    resolve();
                }, delay);
            })
                .then(function() {
                    return promise
                        .then(fn)
                        .then(function(condition) {
                            if (!condition) {
                                return true;
                            }
                            return loop(callNext(), fn)
                        })
                });
        }
        return promise
            .then(fn)
            .then(function(condition) {
                if (!condition) {
                    return true;
                }
                return loop(callNext(), fn)
            })
    }

    return loop(callNext(), conditionChecker);
}


function makeRequest(param) {
    return new Promise(function(resolve, reject) {
        var req = https.request(function(res) {
            var data = '';
            res.on('data', function (chunk) {
                data += chunk;
            });
            res.on('end', function () {
                resolve(data);
            });
        });
        req.on('error', function(e) {
            reject(e);
        });
        req.write(param);
        req.end();
    })
}

function getSomething() {
    var param = 0;

    var limit = 10;

    var results = [];

    function paramGetter() {
        return [param];
    }
    function conditionChecker() {
        return param <= limit;
    }
    function callback(result) {
        results.push(result);
        param++;
    }

    return promiseLoop(makeRequest, paramGetter, conditionChecker, callback)
        .then(function() {
            return results;
        });
}

getSomething().then(function(res) {
    console.log('results', res);
}).catch(function(err) {
    console.log('some error along the way', err);
});


0

यहाँ एक और विधि (ES6 w / std वादा) है। लॉश / अंडरस्कोर टाइप एग्जिट मापदंड (रिटर्न === असत्य) का उपयोग करता है। ध्यान दें कि आप doOne () में चलाने के लिए विकल्पों में आसानी से एक Exitf () विधि जोड़ सकते हैं।

const whilePromise = (fnReturningPromise,options = {}) => { 
    // loop until fnReturningPromise() === false
    // options.delay - setTimeout ms (set to 0 for 1 tick to make non-blocking)
    return new Promise((resolve,reject) => {
        const doOne = () => {
            fnReturningPromise()
            .then((...args) => {
                if (args.length && args[0] === false) {
                    resolve(...args);
                } else {
                    iterate();
                }
            })
        };
        const iterate = () => {
            if (options.delay !== undefined) {
                setTimeout(doOne,options.delay);
            } else {
                doOne();
            }
        }
        Promise.resolve()
        .then(iterate)
        .catch(reject)
    })
};

0

मानक वादा ऑब्जेक्ट का उपयोग करना, और वादा करने के परिणाम वापस करना।

function promiseMap (data, f) {
  const reducer = (promise, x) =>
    promise.then(acc => f(x).then(y => acc.push(y) && acc))
  return data.reduce(reducer, Promise.resolve([]))
}

var emails = []

function getUser(email) {
  return db.getUser(email)
}

promiseMap(emails, getUser).then(emails => {
  console.log(emails)
})

0

पहले वादों की सरणी (वादे की सरणी) लें और बाद में इन वादों के उपयोग को हल करें Promise.all(promisearray)

var arry=['raju','ram','abdul','kruthika'];

var promiseArry=[];
for(var i=0;i<arry.length;i++) {
  promiseArry.push(dbFechFun(arry[i]));
}

Promise.all(promiseArry)
  .then((result) => {
    console.log(result);
  })
  .catch((error) => {
     console.log(error);
  });

function dbFetchFun(name) {
  // we need to return a  promise
  return db.find({name:name}); // any db operation we can write hear
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.