2016-03-03 9 views
6

मैं अनुक्रम में के कई वादों को संसाधित करना चाहता हूं। मेरे पास working piece of code है लेकिन मुझे आश्चर्य है कि क्या मैंने वादे की श्रृंखला को जटिल बना दिया है। मैं नए बंद होने का एक बड़ा सौदा तैयार कर रहा हूं और मैं अपने सिर को खरोंच कर रहा हूं कि क्या मुझे कुछ याद आ रहा है।जावास्क्रिप्ट वादा अनुक्रम

'use strict'; 
addElement("first") 
.then(x => {return addElement("second")}) 
.then(x => { return addElement("third")}) 
.then(x => { return addElement("fourth")}) 

function addElement(elementText){ 
    var myPromise = new Promise(function(resolve,reject){ 
     setTimeout(function(){ 
      var element=document.createElement('H1'); 
      element.innerText = `${elementText} ${Date.now()}`; 
      document.body.appendChild(element); 
      resolve(); 
     }, Math.random() * 2000); 
    }); 
return myPromise; 
} 
+3

अपने तीर कार्यों को सरल बनाया जा सकता है - 'तो फिर (x => addElement (" दूसरी "))' - इसी तरह आप 'addElement' में तीर कार्यों का उपयोग किया जा सकता है - लेकिन मुझे यकीन है कि तुम क्यों लगता है कि नहीं कर रहा हूँ आप "नए बंद होने का एक बड़ा सौदा" बना रहे हैं –

+0

मैं इस मुद्दे में भी भाग गया हूं और इसके बजाय 'बाइंड' का उपयोग कर समाप्त हुआ हूं, हालांकि यह गन्दा लगता है (लेकिन अतिरिक्त फ़ंक्शन रैपर से बचाता है): '.then (addElement.bind (शून्य, "दूसरा")) ', आदि –

+0

बस सोच रहा है कि यहां बनाए गए अनावश्यक वादे ऑब्जेक्ट्स हैं या नहीं। आपके जैसे कुछ 6 वादे ऑब्जेक्ट्स बनाते हैं जब 3 पर्याप्त होगा? फिर पहले से ही एक वादा ऑब्जेक्ट बनाता है जिसे आप फिर से उपयोग नहीं कर सकते? मुझे लगता है कि मैं गलत हो सकता है। – Nishant

उत्तर

3

आपका कोड सबसे अच्छा आप यहाँ प्राप्त कर सकते के करीब लग रहा है:

इस समारोह लिखने के लिए एक बेहतर तरीका है। वादा करने के लिए वादा एक अजीब संरचना हो सकती है, विशेष रूप से लिखने के रूप में प्रोमिस-इफिड कोड अक्सर किसी अन्य फ़ंक्शन में फ़ंक्शन एम्बेड करना समाप्त कर सकता है। जैसा कि आप here देख सकते हैं, यह उपयोग करने के लिए एक बहुत ही आम phrasing है। केवल दो स्टाइलिस्ट बदलाव हैं जिन्हें संभवतः बनाया जा सकता है। सबसे पहले, myPromise अनावश्यक है और केवल कोड की भ्रमित अतिरिक्त पंक्ति जोड़ने में कार्य करता है। सीधे वादे वापस करने के लिए यह आसान है। दूसरा, आप शुरुआत में अपनी कॉल को सरल बनाने के लिए फ़ंक्शन बाइंडिंग का उपयोग कर सकते हैं। यह फ़ंक्शन के अंदर ही नहीं हो सकता है, लेकिन यह कई बंदियों को खत्म करता है। दोनों परिवर्तन नीचे दिखाया गया हैं:

'use strict'; 
var myWait = waitRand.bind(null,2000); 
myWait 
    .then(addElement.bind(null, "first")) 
    .then(myWait) 
    .then(addElement.bind(null, "second")) 
    .then(myWait) 
    .then(addElement.bind(null, "third")) 

function waitRand(millis) { 
    return new Promise((resolve, reject) => { 
    setTimeout(resolve, Math.random() * millis); 
    } 
} 

function addElement(elementText) { 
    var element = document.createElement('h1'); 
    element.innerText = `${elementText} ${Date.now()}`; 
    document.body.appendChild(element); 
} 

इस कारोबार वादा श्रृंखला की लंबाई:

'use strict'; 
addElement("first") 
.then(addElement.bind(null,"second")) 
.then(addElement.bind(null,"third")) 
.then(addElement.bind(null,"fourth")) 

function addElement(elementText){ 
    return new Promise(function(resolve,reject){ 
     setTimeout(function(){ 
      var element=document.createElement('H1'); 
      element.innerText = `${elementText} ${Date.now()}`; 
      document.body.appendChild(element); 
      resolve(); 
     }, Math.random() * 2000); 
    }); 
} 

यह उनका कहना है कि, अगर आप थोड़ा पुनर्गठन के लिए तैयार थे, साथ ही कुछ अधिक आकर्षक डिजाइन रूप ले जाएगा लायक है स्पष्टता के लिए, साथ ही साथ थोड़ा कम घोंसला वाले स्तर भी हैं।

6

@TheToolBox आपके लिए एक अच्छा जवाब है।

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

Promise.prototype.bind = Promise.prototype.then; 

const coro = g => { 
    const next = x => { 
    let {done, value} = g.next(x); 
    return done ? value : value.bind(next); 
    } 
    return next(); 
} 

कि का उपयोग करना, अपने कोड वहाँ कुछ बहुत दिलचस्प बातें आप वादे के साथ जनरेटर का उपयोग कर सकते हैं इस

const addElement = elementText => 
    new Promise(resolve => { 
    setTimeout(() => { 
     var element = document.createElement('H1'); 
     element.innerText = `${elementText} ${Date.now()}`; 
     document.body.appendChild(element); 
     resolve(); 
    }, Math.random() * 2000); 
    }); 

coro(function*() { 
    yield addElement('first'); 
    yield addElement('second'); 
    yield addElement('third'); 
    yield addElement('fourth'); 
}()); 

तरह दिखाई देगा। वे यहां तुरंत स्पष्ट नहीं हैं क्योंकि आपके addElement वादे किसी वास्तविक मूल्य को हल नहीं करता है।


आप वास्तव में resolve कुछ मान, आप की तरह

// sync 
const appendChild = (x,y) => x.appendChild(y); 

// sync 
const createH1 = text => { 
    var elem = document.createElement('h1'); 
    elem.innerText = `${text} ${Date.now()}`; 
    return elem; 
}; 

// async 
const delay = f => 
    new Promise(resolve => { 
    setTimeout(() => resolve(f()), Math.random() * 2000); 
    }); 

// create generator; this time it has a name and accepts an argument 
// mix and match sync/async as needed 
function* renderHeadings(target) { 
    appendChild(target, yield delay(() => createH1('first'))); 
    appendChild(target, yield delay(() => createH1('second'))); 
    appendChild(target, yield delay(() => createH1('third'))); 
    appendChild(target, yield delay(() => createH1('fourth'))); 
} 

// run the generator; set target to document.body 
coro(renderHeadings(document.body)); 

टिप्पण लायक कुछ कर सकते हैं, तो createH1 और appendChild तुल्यकालिक कार्य हैं। यह दृष्टिकोण आपको प्रभावी रूप से सामान्य कार्यों को एक साथ करने और सिंक के बीच की रेखाओं को धुंधला करने और एसिंक के बीच की रेखाओं को धुंधला करने की अनुमति देता है। यह मूल रूप से आपके द्वारा पोस्ट किए गए कोड की तरह ही निष्पादित/व्यवहार करता है।

तो हाँ, यह अंतिम कोड उदाहरण थोड़ा और दिलचस्प हो सकता है।


अन्त में,

एक विशिष्ट लाभ coroutine .then श्रृंखलन से अधिक है, कि संकल्प लिया वादे के सभी एक ही दायरे के अंदर पहुँचा जा सकता है है।

.then जंजीरों ...

op1() 
    .then(x => op2(x)) 
    .then(y => op3(y)) // cannot read x here 
    .then(z => lastOp(z)) // cannot read x or y here 

coroutine करने के लिए ...

function*() { 
    let x = yield op1(); // can read x 
    let y = yield op2(); // can read x and y here 
    let z = yield op3(); // can read x, y, and z here 
    lastOp([x,y,z]);  // use all 3 values ! 
} 
बेशक

की तुलना में इस वादे का उपयोग कर के लिए workarounds हैं, लेकिन ओह लड़का यह बदसूरत तेजी से प्राप्त करता है ...


यदि आप इस तरह जेनरेटर का उपयोग करने में रुचि रखते हैं, तो मैं आपको सलाह देता हूं कि आप co प्रोजेक्ट चेकआउट करें।

और यहाँ एक लेख, Callbacks vs Coroutines, सह, @tj के निर्माता से है।

वैसे भी, मैं तुम्हें मजा कुछ अन्य तकनीकों के बारे में सीखने की थी आशा ^__^

3

आप addElement() वापसी एक समारोह बनाने के बजाय तो यह सीधे बिना .then() संचालकों में डाला जा सकता द्वारा अपने कार्य के उपयोग को आसान बनाने में कर सकता है अज्ञात फ़ंक्शन बनाने के लिए:

'use strict'; 
addElement("first")() 
    .then(addElement("second")) 
    .then(addElement("third")) 
    .then(addElement("fourth")) 

function addElement(elementText){ 
    return function() { 
     return new Promise(function(resolve){ 
      setTimeout(function(){ 
       var element=document.createElement('H1'); 
       element.innerText = `${elementText} ${Date.now()}`; 
       document.body.appendChild(element); 
       resolve(); 
      }, Math.random() * 2000); 
     }); 
    } 
} 
1

बंद होने की संख्या के संबंध में बहुत कुछ नहीं किया जाना चाहिए। कार्यों का घोंसला सिर्फ कुछ है जो आप जेएस के साथ उपयोग करते हैं, और सवाल में कोड वास्तव में बुरा नहीं है।

जैसा कि अन्य ने कहा है, एक समारोह को वापस करने के लिए addElement() लिखना एक स्वच्छ मुख्य वादा श्रृंखला के लिए बनाता है।

थोड़ा आगे जाकर, आप लौटे हुए फ़ंक्शन को एक आंतरिक वादे श्रृंखला के साथ लिखने पर विचार कर सकते हैं, जिससे डीओएम तत्व सम्मिलन से वादा संकल्प (मामूली) पृथक्करण की अनुमति मिलती है। इससे कोई और कम बंद नहीं होता है, लेकिन यह वाक्य रचनात्मक रूप से neater है, विशेष रूप से आपको setTimeout(resolve, Math.random() * 2000); लिखने की अनुमति देता है।

'use strict'; 
addElement("first") 
.then(addElement("second")) 
.then(addElement("third")) 
.then(addElement("fourth")); 

function addElement(elementText) { 
    return function() { 
     return new Promise(function(resolve, reject) { 
      setTimeout(resolve, Math.random() * 2000); 
     }).then(function() { 
      var element = document.createElement('H1'); 
      document.body.appendChild(element); 
      element.innerText = `${elementText} ${Date.now()}`; 
     }); 
    }; 
} 

हो सकता है कि यह सिर्फ मुझे है, लेकिन मैं इस और अधिक आकर्षक आंखों पर, यद्यपि एक अतिरिक्त तो फिर (की कीमत पर), इसलिए एक अतिरिक्त वादा, addElement() प्रति पाते हैं।

नोट: यदि आपको किसी मूल्य के साथ वादे को हल करने की आवश्यकता है, तो आपको अभी भी जंजीर के कॉलबैक से मूल्य वापस कर ऐसा करने का मौका दिया जाता है।

भी आगे जा रहे हैं, तो आप डाला तत्वों में प्रकट करना चाहते हैं आदेश की मांग की, नहीं वादा निपटान द्वारा निर्धारित क्रम है, तो आप/बनाने तुल्यकालिक तत्वों डालें, और उन्हें अतुल्यकालिक रूप से पॉप्युलेट कर सकते हैं:

function addElement(elementText) { 
    var element = document.createElement('H1'); 
    document.body.appendChild(element); 
    return function() { 
     return new Promise(function(resolve, reject) { 
      setTimeout(resolve, Math.random() * 2000); 
     }).then(function() { 
      element.innerText = `${elementText} ${Date.now()}`; 
     }); 
    }; 
} 

element.innerText = ... लाइन छोड़ने के दौरान सम्मिलन के समय को बदलने के लिए, addElement() के भीतर दो लाइनों को स्थानांतरित करना आवश्यक था। यह संभव है कि आप आंतरिक वादे श्रृंखला का चयन करें या नहीं।

Sequence = { 
    all(steps) { 
     var promise = Promise.resolve(), 
      results = []; 

     const then = i => { 
      promise = promise.then(() => { 
       return steps[ i ]().then(value => { 
        results[ i ] = value; 
       }); 
      }); 
     }; 

     steps.forEach((step, i) => { 
      then(i); 
     }); 

     return promise.then(() => Promise.resolve(results)); 
    }, 
    race(steps) { 
     return new Promise((resolve, reject) => { 
      var promise = Promise.reject(); 

      const c = i => { 
       promise = promise.then(value => { 
        resolve(value); 
       }).catch(() => { 
        return steps[ i ](); 
       }); 
      }; 

      steps.forEach((step, i) => { 
       c(i); 
      }); 

      promise.catch(() => { 
       reject(); 
      }); 
     }); 
    } 
}; 

Sequence.all करेंगे:

+0

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

3

मुझे यकीन है कि क्यों दूसरों को बाहर एक आसान तरीका बाहर छोड़ दिया, तो आप बस एक सरणी और reduce विधि इस्तेमाल कर सकते हैं

let promise, inputArray = ['first', 'second', 'third', 'fourth']; 

promise = inputArray.reduce((p, element) => p.then(() => addElement(element)), Promise.resolve()); 
+1

आपको 'p = p.then असाइन करने की आवश्यकता नहीं है (...' –

0

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

Sequence.all([() => { 
    return Promise.resolve('a'); 
},() => { 
    return Promise.resolve('b'); 
} ]).then(values => { 
    console.log(values); // output [ 'a', 'b' ] 
}); 

अनुक्रमांक अनुक्रम में कार्य चलाएगा और एक वादे ऑब्जेक्ट को हल करने के दौरान चलना बंद कर देगा।

Sequence.race([() => { 
    return Promise.reject('a'); 
},() => { 
    return Promise.resolve('b'); 
},() => { 
    return Promise.resolve('c'); 
} ]).then(values => { 
    console.log(values); // output [ 'a' ] 
}); 
संबंधित मुद्दे