मैं क्रमिक क्रमों में वादों को कैसे निष्पादित कर सकता हूं?


81

मेरे पास कई वादे हैं जिन्हें क्रमबद्ध रूप से चलाने की आवश्यकता है।

var promises = [promise1, promise2, ..., promiseN];

RSVP.all पर कॉल करने से उन्हें समानांतर में निष्पादित किया जाएगा:

RSVP.all(promises).then(...); 

लेकिन, मैं उन्हें अनुक्रम में कैसे चला सकता हूं?

मैं मैन्युअल रूप से उन्हें इस तरह से ढेर कर सकता हूं

RSVP.resolve()
    .then(promise1)
    .then(promise2)
    ...
    .then(promiseN)
    .then(...);

लेकिन समस्या यह है कि वादों की संख्या भिन्न होती है और वादों की सरणी गतिशील रूप से निर्मित होती है।


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

इसी तरह का प्रश्न लेकिन फ्रेमवर्क-विशिष्ट नहीं: stackoverflow.com/q/24586110/245966
jakub.g

जवाबों:


136

यदि आपके पास पहले से ही एक सरणी में है तो वे पहले से ही निष्पादित कर रहे हैं। यदि आपके पास एक वादा है तो यह पहले से ही निष्पादित है। यह वादों की चिंता नहीं है (IE वे विधि के Taskसाथ उस संबंध में C # s की तरह नहीं हैं .Start())। .allकुछ भी निष्पादित नहीं करता है यह सिर्फ एक वादा लौटाता है।

यदि आपके पास वादे लौटाने के कार्य हैं:

var tasks = [fn1, fn2, fn3...];

tasks.reduce(function(cur, next) {
    return cur.then(next);
}, RSVP.resolve()).then(function() {
    //all executed
});

या मान:

var idsToDelete = [1,2,3];

idsToDelete.reduce(function(cur, next) {
    return cur.then(function() {
        return http.post("/delete.php?id=" + next);
    });
}, RSVP.resolve()).then(function() {
    //all executed
});

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

आपके उत्तर के लिए धन्यवाद। आप सही हैं कि पहले से ही एक वादा करने का मतलब है कि यह क्रियान्वित हो रहा है इसलिए मेरा प्रश्न सही तरीके से नहीं बना था। मैंने वादों के बिना अपनी समस्या को अलग तरीके से हल किया।
जाकारव

1
@ सबसे पहले अच्छी तरह से, वाट। दूसरे, पिछली प्रतिक्रिया को पारित कर दिया जाता है .then, इस उदाहरण में इसे केवल नजरअंदाज कर दिया जाता है ...
एस्लेइजा

3
यदि इन वादों में से कोई भी विफल हो जाता है तो त्रुटि को कभी भी अस्वीकार नहीं किया जाएगा और वादा कभी भी हल नहीं होगा ...
मैक्सवेल

5
यदि आपके पास पहले से ही एक सरणी में है तो वे पहले से ही निष्पादित कर रहे हैं। - यह वाक्यांश बोल्ड + बड़े फ़ॉन्ट में होना चाहिए। यह समझना महत्वपूर्ण है।
डुकिन

22

ECMAScript 2017 async फ़ंक्शन के साथ यह इस तरह किया जाएगा:

async function executeSequentially() {
    const tasks = [fn1, fn2, fn3]

    for (const fn of tasks) {
        await fn()
    }
}

आप अब Async फ़ंक्शन का उपयोग करने के लिए BabelJS का उपयोग कर सकते हैं


यह अब (2020) तक डिफ़ॉल्ट दृष्टिकोण होना चाहिए। पहली बार उपयोगकर्ताओं के लिए यहां दो बातें नोट करना महत्वपूर्ण हो सकता है: 1. एक बार एक वादा मौजूद होने के बाद, यह पहले से ही चल रहा है। तो यह वास्तव में महत्वपूर्ण है कि 2. fn1, fn2, fn3यहाँ फ़ंक्शन हैं जैसे () => yourFunctionReturningAPromise()कि बस के विपरीत yourFunctionReturningAPromise()। यही कारण है कि await fn()सिर्फ इसके बजाय आवश्यक क्यों है await fnआधिकारिक डॉक्स में और देखें । एक टिप्पणी के रूप में पोस्ट करने के लिए क्षमा करें, लेकिन संपादित कतार पूर्ण है :)
ezmegy

7

2017 में ईएस 7 रास्ता।

  <script>
  var funcs = [
    _ => new Promise(resolve => setTimeout(_ => resolve("1"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("2"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("3"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("4"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("5"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("6"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("7"), 1000))
  ];
  async function runPromisesInSequence(promises) {
    for (let promise of promises) {
      console.log(await promise());
    }
  }
  </script>
  <button onClick="runPromisesInSequence(funcs)">Do the thing</button>

यह दिए गए कार्यों को क्रमिक रूप से निष्पादित करेगा (एक-एक करके), समानांतर में नहीं। पैरामीटर फ़ंक्शन promisesका एक सरणी है, जो वापस लौटता है Promise

उपरोक्त कोड के साथ प्लंकर उदाहरण: http://plnkr.co/edit/UP0rhD?p=preview


4

एक उत्तर में दूसरा प्रयास जिसमें मैं और अधिक व्याख्यात्मक होने की कोशिश करता हूं:

सबसे पहले, RSVP README से कुछ अपेक्षित पृष्ठभूमि :

वास्तव में भयानक हिस्सा तब आता है जब आप पहले हैंडलर से एक वादा वापस करते हैं ... यह आपको नेस्टेड कॉलबैक को समतल करने की अनुमति देता है, और उन वादों की मुख्य विशेषता है जो बहुत अतुल्यकालिक कोड वाले कार्यक्रमों में "सही बहाव" को रोकता है।

यह ठीक है कि आप वादों को क्रमिक कैसे बनाते हैं, बाद में उस वादे से किया गया वादा वापस करके thenजो इसके पहले पूरा होना चाहिए।

पेड़ के रूप में वादों के ऐसे सेट के बारे में सोचना मददगार है, जहां शाखाएं अनुक्रमिक प्रक्रियाओं का प्रतिनिधित्व करती हैं, और पत्तियां समवर्ती प्रक्रियाओं का प्रतिनिधित्व करती हैं।

इस तरह के वादों के वृक्ष के निर्माण की प्रक्रिया अन्य प्रकार के वृक्षों के निर्माण के बहुत सामान्य कार्य के अनुरूप होती है: जहाँ आप वर्तमान में शाखाओं को जोड़ रहे हैं, उस स्थान पर एक सूचक या संदर्भ बनाए रखें और पुनरावृत्तियाँ इसमें जोड़ें।

जैसा कि @Esailija ने अपने उत्तर में बताया, यदि आपके पास वादे-प्रतिफल करने वाले कार्यों की एक सरणी है, तो वे तर्क नहीं लेते हैं जिनका उपयोग आप reduceबड़े करीने से आपके लिए पेड़ बनाने के लिए कर सकते हैं। यदि आपने कभी खुद के लिए कम लागू किया है, तो आप समझेंगे कि @ एस्लेइजा के जवाब में पर्दे के पीछे क्या कम हो रहा है, वर्तमान वादे ( cur) के संदर्भ को बनाए रख रहा है और प्रत्येक वादा अपने अगले वादे को वापस कर रहा है then

यदि आपके पास सजातीय का एक अच्छा सरणी नहीं है (वे उन दलीलों के संबंध में / जो वे लेते हैं / वापस लौटते हैं) वादे वापस करने का कार्य करते हैं, या यदि आपको एक सरल रैखिक अनुक्रम की तुलना में अधिक जटिल संरचना की आवश्यकता है, तो आप खुद को बनाए रखकर वादों के पेड़ का निर्माण कर सकते हैं वादे के पेड़ की स्थिति का संदर्भ जहां आप नए वादे जोड़ना चाहते हैं:

var root_promise = current_promise = Ember.Deferred.create(); 
// you can also just use your first real promise as the root; the advantage of  
// using an empty one is in the case where the process of BUILDING your tree of 
// promises is also asynchronous and you need to make sure it is built first 
// before starting it

current_promise = current_promise.then(function(){
  return // ...something that returns a promise...;
});

current_promise = current_promise.then(function(){
  return // ...something that returns a promise...;
});

// etc.

root_promise.resolve();

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

आप यह भी सुनिश्चित करने के लिए Ember.run.scheduleOnce ('afterRender') का उपयोग कर सकते हैं ताकि यह सुनिश्चित किया जा सके कि अगले वादे को पूरा करने से पहले एक वादे में कुछ किया जाता है - मेरा नीचा-से-बहुत जटिल जवाब भी इसका एक उदाहरण दिखाता है।


3
यह बहुत बेहतर है, हालांकि मुझे लगता है कि आप अभी भी विषय से बाहर जा रहे हैं। यह वादों पर कई उत्तरों के लिए आम है, लोगों को प्रश्न पढ़ने में समय नहीं लगता है, इसके बजाय वे बस वादों के कुछ पहलू पर टिप्पणी करते हैं जो वे व्यक्तिगत रूप से समझते हैं। मूल प्रश्न में समानांतर निष्पादन शामिल नहीं है, थोड़ा सा भी नहीं है, और यह स्पष्ट रूप से दिखाता है कि बस के माध्यम thenसे वांछित है, आपने बहुत सी अतिरिक्त जानकारी दी है जो उस प्रश्न के उत्तर को छिपा रही है जो पूछा गया था।
डेविड मैकमुलिन

@DavidMcMullin ".... और यह स्पष्ट रूप से दिखाता है कि बस के माध्यम से पीछा करना वांछित है ..." लेकिन वास्तव में वह बताता है कि वादों के क्रम को गतिशील रूप से बनाया गया है। तो उसे यह समझने की ज़रूरत है कि पेड़ कैसे बनाया जाए, भले ही इस मामले में यह पेड़ का सरल उपसमूह "रैखिक अनुक्रम" हो। आपको अभी भी श्रृंखला में अंतिम वादे के संदर्भ को बनाए रखने और नए वादों को जोड़कर इसे बनाना होगा।
माइकल जॉन्सटन

जब ओपी ने कहा "वादों की संख्या भिन्न होती है और वादों की सरणी गतिशील रूप से निर्मित होती है", मुझे पूरा यकीन है कि सभी का मतलब है कि वह यह है कि सरणी का आकार पूर्व निर्धारित नहीं था और इसलिए वह एक सरल का उपयोग नहीं कर सका Promise.resolve().then(...).then(...)...ऐसा नहीं है कि वादे निष्पादित करते समय सरणी बढ़ रही थी । बेशक, यह सब अब लूट है।
JLRishe

4

अभी तक एक और दृष्टिकोण प्रोटोटाइप पर एक वैश्विक अनुक्रम फ़ंक्शन को परिभाषित करना है Promise

Promise.prototype.sequence = async (promiseFns) => {
  for (let promiseFn of promiseFns) {
    await promiseFn();
  }
}

फिर आप इसे कहीं भी, जैसे चाहे उपयोग कर सकते हैं Promise.all()

उदाहरण

const timeout = async ms => new Promise(resolve =>
  setTimeout(() => {
    console.log("done", ms);
    resolve();
  }, ms)
);

// Executed one after the other
await Promise.sequence([() => timeout(1000), () => timeout(500)]);
// done: 1000
// done: 500

// Executed in parallel
await Promise.all([timeout(1000), timeout(500)]);
// done: 500
// done: 1000

अस्वीकरण: सावधान रहें संपादन प्रोटोटाइप!


2

सभी को हल करने के लिए आवश्यक है कि एक forलूप है :)

var promises = [a,b,c];
var chain;

for(let i in promises){
  if(chain) chain = chain.then(promises[i]);
  if(!chain) chain = promises[i]();
}

function a(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve A');
      resolve();
    },1000);
  });
}
function b(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve B');
      resolve();
    },500);
  });
}
function c(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve C');
      resolve();
    },100);
  });
}

क्यों करता है if(!chain) chain = promises[i]();एक है ()अंत में? मुझे लगता है कि उस मामले में जहां श्रृंखला खाली है (पुनरावृत्ति 0) एक बस कच्चा वादा करना चाहेगा, और फिर लूप श्रृंखला के प्रत्येक बाद के वादे को इंजेक्ट कर सकता है .then()। इस प्रकार, यह नहीं होगा if(!chain) chain = promises[i];? शायद मुझे यहाँ कुछ समझ में नहीं आया है।
halfer

आह - आपके a,b,cवास्तव में वादे लौटाने वाले कार्य हैं, और वादे नहीं। तो उपरोक्त समझ में आता है। लेकिन इस तरह से प्रोमिस को लपेटने में क्या उपयोगिता है?
halfer

2

मुझे इसी तरह की समस्या थी, और मैंने एक पुनरावर्ती कार्य किया जो क्रमबद्ध रूप से एक-एक करके कार्य करता है।

var tasks = [fn1, fn2, fn3];

var executeSequentially = function(tasks) {
  if (tasks && tasks.length > 0) {
    var task = tasks.shift();

    return task().then(function() {
      return executeSequentially(tasks);
    });
  }

  return Promise.resolve();  
};

मामले में आपको इन कार्यों से आउटपुट एकत्र करने की आवश्यकता है:

var tasks = [fn1, fn2, fn3];

var executeSequentially = function(tasks) {
  if (tasks && tasks.length > 0) {
    var task = tasks.shift();

    return task().then(function(output) {
      return executeSequentially(tasks).then(function(outputs) {
        outputs.push(output);

        return Promise.resolve(outputs);  
      });
    });
  }

  return Promise.resolve([]);
};

0
export type PromiseFn = () => Promise<any>;

export class PromiseSequence {
  private fns: PromiseFn[] = [];

  push(fn: PromiseFn) {
    this.fns.push(fn)
  }

  async run() {
    for (const fn of this.fns) {
      await fn();
    }
  }
}

फिर

const seq = new PromiseSequence();
seq.push(() => Promise.resolve(1));
seq.push(() => Promise.resolve(2));
seq.run();

यह भी संभव है कि किसी अन्य निजी संस्करण में कौन से वादे वापस किए जाएं और इसे कॉलबैक में पारित किया जाए


-1

मैं जिस चीज के बाद था वह अनिवार्य रूप से मैपसरीज थी, और मैं मानों के एक सेट पर मैपिंग सेव करने के लिए होता हूं, और मैं परिणाम चाहता हूं।

इसलिए, यहां तक ​​मुझे मिला है, भविष्य में इसी तरह की चीजों की खोज करने में दूसरों की मदद करने के लिए।

(ध्यान दें कि संदर्भ एक एम्बर एप्लिकेशन है)।

App = Ember.Application.create();

App.Router.map(function () {
    // put your routes here
});

App.IndexRoute = Ember.Route.extend({
    model: function () {
            var block1 = Em.Object.create({save: function() {
                return Em.RSVP.resolve("hello");
            }});
    var block2 = Em.Object.create({save: function() {
            return Em.RSVP.resolve("this");
        }});
    var block3 = Em.Object.create({save: function() {
        return Em.RSVP.resolve("is in sequence");
    }});

    var values = [block1, block2, block3];

    // want to sequentially iterate over each, use reduce, build an array of results similarly to map...

    var x = values.reduce(function(memo, current) {
        var last;
        if(memo.length < 1) {
            last = current.save();
        } else {
            last = memo[memo.length - 1];
        }
        return memo.concat(last.then(function(results) {
            return current.save();
        }));
    }, []);

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