ফাংশন পুনরায় শুরু করার আগে কোনও জাভাস্ক্রিপ্ট প্রতিশ্রুতি সমাধানের জন্য কীভাবে অপেক্ষা করবেন?


113

আমি কিছু ইউনিট পরীক্ষা করছি। পরীক্ষার কাঠামোটি একটি পৃষ্ঠাকে একটি আইফ্রেমে লোড করে এবং তারপরে সেই পৃষ্ঠাটির বিরুদ্ধে দৃ runs়তা দেয়। প্রতিটি পরীক্ষা শুরুর আগে আমি এমন একটি তৈরি করি Promiseযা আইফ্রেমের onloadইভেন্টটি কল করার জন্য resolve()সেট করে, আইফ্রেমের সেট করে srcএবং প্রতিশ্রুতি ফিরিয়ে দেয়।

সুতরাং, আমি কেবল কল করতে পারি loadUrl(url).then(myFunc), এবং এটি যা হবে তা কার্যকর করার আগে পৃষ্ঠাটি লোড হওয়ার জন্য অপেক্ষা করবে myFunc

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

এই ডিজাইনের ক্ষতিটি হ'ল আমি ক্রমাগত এগুলিতে কয়েকটি লাইনের কোড সহ বেনামে ফাংশন লিখছি। আরও, যখন আমার একটি ওয়ার্ক-এয়ার (কুইনাইটস assert.async()) রয়েছে, টেস্ট ফাংশন যা প্রতিশ্রুতিগুলি সংশোধন করে প্রতিশ্রুতি সঞ্চালনের আগেই সম্পূর্ণ হয়।

আমি ভাবছি যে Promiseএটি নেট না হওয়া অবধি সমাধান না হওয়া অবধি অপেক্ষার (ব্লক / স্লিপ) কোনও মূল্য পাওয়ার উপায় আছে কিনা IAsyncResult.WaitHandle.WaitOne()? আমি জানি জাভাস্ক্রিপ্ট একক থ্রেডযুক্ত, তবে আমি আশা করছি এর অর্থ এই নয় যে কোনও ফাংশন ফলন করতে পারে না।

সংক্ষেপে, সঠিক ক্রমে ফলাফলগুলি ছড়িয়ে দেওয়ার জন্য এখানে কী কী উপায় আছে?

function kickOff() {
  return new Promise(function(resolve, reject) {
    $("#output").append("start");
    
    setTimeout(function() {
      resolve();
    }, 1000);
  }).then(function() {
    $("#output").append(" middle");
    return " end";
  });
};

function getResultFrom(promise) {
  // todo
  return " end";
}

var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>


আপনি যদি অ্যাপেনড কলগুলিকে পুনরায় ব্যবহারযোগ্য ফাংশনে রেখে দেন তবে আপনি () শুকনোর প্রয়োজন অনুসারে করতে পারেন। আপনার কাছে মাল্টি উদ্দেশ্য হ্যান্ডেলার করতে পারেন, দ্বারা বাহিত এই তারপর () কল, মত ভোজন .then(fnAppend.bind(myDiv)), যা অতি anons নিচে কাটা করতে পারেন।
ডান্ডাভিস

আপনি কি দিয়ে পরীক্ষা করছেন? যদি এটি একটি আধুনিক ব্রাউজার হয় বা আপনি আপনার কোডটি ট্রাইপেল করতে বাবেলজেএস এর মতো কোনও সরঞ্জাম ব্যবহার করতে পারেন তবে এটি অবশ্যই সম্ভব।
বেনিয়ামিন গ্রুইনবাউম

উত্তর:


81

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

ব্রাউজারে জাভাস্ক্রিপ্ট বর্তমান প্রজন্মের একটি নেই wait()বা sleep()যে run অন্যান্য জিনিষ পারেন। সুতরাং, আপনি যা চাইছেন তা কেবল আপনি করতে পারবেন না। পরিবর্তে, এতে অ্যাসিঙ্ক অপারেশন রয়েছে যা তাদের কাজ করবে এবং তারপরে আপনাকে ডেকে আনে (যেমন আপনি প্রতিশ্রুতি ব্যবহার করছেন)।

এর একটি অংশ জাভাস্ক্রিপ্টের একক থ্রেডডনেসের কারণে। যদি একক থ্রেড স্পিনিং হয়, তবে স্পিনিং থ্রেডটি না করা পর্যন্ত অন্য কোনও জাভাস্ক্রিপ্ট কার্যকর করা যাবে না। ES6 প্রবর্তন করে yieldএবং জেনারেটর দেয় যা এর মতো কিছু সহযোগিতামূলক কৌশলগুলি অনুমোদন দেবে, তবে আমরা ইনস্টল করা ব্রাউজারগুলির বিস্তৃত swatch এগুলি ব্যবহার করতে সক্ষম হবার বেশ উপায় ways যে ব্যবহার করা হচ্ছে)।


প্রতিশ্রুতি-ভিত্তিক কোডের যত্ন সহকারে পরিচালনা অনেক অ্যাসিঙ্ক ক্রিয়াকলাপের জন্য মৃত্যুদণ্ডের আদেশ নিয়ন্ত্রণ করতে পারে।

আমি নিশ্চিত না যে আপনি নিজের কোডটিতে কী অর্ডারটি অর্জন করতে চাইছেন তা আমি ঠিক বুঝতে পেরেছি তবে আপনি আপনার বিদ্যমান kickOff()ফাংশনটি ব্যবহার করে এই জাতীয় কিছু করতে পারেন এবং তারপরে .then()ফোন করার পরে কোনও হ্যান্ডলারের সাথে সংযুক্ত করে:

function kickOff() {
  return new Promise(function(resolve, reject) {
    $("#output").append("start");

    setTimeout(function() {
      resolve();
    }, 1000);
  }).then(function() {
    $("#output").append(" middle");
    return " end";
  });
}

kickOff().then(function(result) {
    // use the result here
    $("#output").append(result);
});

এটি গ্যারান্টিযুক্ত ক্রমে আউটপুট ফিরিয়ে দেবে - এর মতো:

start
middle
end

2018 এ আপডেট করুন (এই উত্তরটি লেখার তিন বছর পরে):

আপনি যদি হয় নিজের কোডটি পরিবহন করেন বা আপনার কোডটি এমন পরিবেশে চালিত করেন যা ES7 বৈশিষ্ট্যগুলিকে সমর্থন করে asyncএবং যেমন await, আপনি এখন awaitকোনও প্রতিশ্রুতির ফলাফলের জন্য অপেক্ষা করতে আপনার কোডটি "উপস্থিত" করতে ব্যবহার করতে পারেন। এটি প্রতিশ্রুতি নিয়ে এখনও বিকাশ করছে। এটি এখনও সমস্ত জাভাস্ক্রিপ্টকে অবরুদ্ধ করে না, তবে এটি আপনাকে বন্ধুত্বপূর্ণ বাক্য গঠনতে ক্রমিক ক্রিয়াকলাপ লেখার অনুমতি দেয়।

ES6 উপায় করার পরিবর্তে:

someFunc().then(someFunc2).then(result => {
    // process result here
}).catch(err => {
    // process error here
});

তুমি এটি করতে পারো:

// returns a promise
async function wrapperFunc() {
    try {
        let r1 = await someFunc();
        let r2 = await someFunc2(r1);
        // now process r2
        return someValue;     // this will be the resolved value of the returned promise
    } catch(e) {
        console.log(e);
        throw e;      // let caller know the promise was rejected with this reason
    }
}

wrapperFunc().then(result => {
    // got final result
}).catch(err => {
    // got error
});

4
ঠিক আছে, then()আমি যা করছি তা ব্যবহার করে using আমি শুধু function() { ... }সব সময় লিখতে পছন্দ করি না । এটি কোড আপ clutters।
dx_over_dt

4
@dfoverdx - জাভাস্ক্রিপ্টে async কোডিং সর্বদা কলব্যাক জড়িত তাই আপনাকে সর্বদা একটি ফাংশন (বেনামে বা নামকরণ) সংজ্ঞায়িত করতে হবে। বর্তমানে এটির কোনও উপায় নেই।
jفر00

4
যদিও আপনি এটিটি আরও ছড়িয়ে দিতে ES6 তীর চিহ্ন ব্যবহার করতে পারেন। (foo) => { ... }পরিবর্তেfunction(foo) { ... }
রব এইচ

4
@ রব মন্তব্যটিতে প্রায়শই যুক্ত করা আপনি foo => single.expression(here)কোঁকড়ানো এবং বৃত্তাকার বন্ধনী থেকে মুক্তি পেতে লিখতেও পারেন ।
ভিজি

4
@dfoverdx - আমার উত্তরের তিন বছর পরে, আমি ইএস 7 asyncএবং awaitসিনট্যাক্সের সাহায্যে এটি একটি বিকল্প হিসাবে আপডেট করেছি যা এখন নোড.জেজে এবং আধুনিক ব্রাউজারগুলিতে বা ট্রান্সপোর্টারগুলির মাধ্যমে উপলভ্য।
jender00

14

ES2016 ব্যবহার করে আপনি ব্যবহার করতে পারেন asyncএবং এর awaitমতো কিছু করতে পারেন:

(async () => {
  const data = await fetch(url)
  myFunc(data)
}())

ES2015 ব্যবহার করে আপনি জেনারেটর ব্যবহার করতে পারেন । আপনি বিমূর্ত এটা দূরে একটি ব্যবহার করতে পারেন সিনট্যাক্স পছন্দ না হয় asyncইউটিলিটি ফাংশন যেমন এখানে ব্যাখ্যা

ES5 ব্যবহার করে আপনি সম্ভবত ব্লুবার্ডের মতো একটি লাইব্রেরি চাইলে আপনাকে আরও নিয়ন্ত্রণ দিন।

অবশেষে, যদি আপনার রানটাইম ইএস2015 সমর্থন করে তবে ইতিমধ্যে কার্যকর করার আদেশটি ফেচ ইঞ্জেকশন ব্যবহার করে সমান্তরালতার সাথে সংরক্ষণ করা যেতে পারে ।


4
আপনার জেনারেটরের উদাহরণটি কাজ করে না, এটিতে রানারের অভাব রয়েছে। আপনি কেবল ইএস 8 async/ ট্রান্সপ্লাই করতে পারবেন দয়া করে আর জেনারেটরদের আর প্রস্তাব করবেন না await
বার্গি

4
আপনার প্রতিক্রিয়ার জন্য ধন্যবাদ। আমি জেনারেটরের উদাহরণটি সরিয়েছি এবং এর পরিবর্তে সম্পর্কিত ওএসএস লাইব্রেরির সাথে আরও বিস্তৃত জেনারেটর টিউটোরিয়ালের সাথে লিঙ্ক করেছি।
জোশ হাবদাস

12
এই কেবল প্রতিশ্রুতি আবৃত। আপনি যখন এটি চালাবেন তখন অ্যাসিঙ্ক ফাংশন কোনও কিছুর জন্য অপেক্ষা করবে না , এটি কেবল একটি প্রতিশ্রুতি ফিরিয়ে দেবে। async/ এর awaitজন্য অন্য একটি সিনট্যাক্স Promise.then
rustyx

আপনি একেবারে ঠিক আছেন asyncঅপেক্ষা করবেন না ... যদি না এটি ব্যবহার করে ফল দেয়await । ES2016 / ES7 উদাহরণটি এখনও এসও চালানো যায় না তাই এই আচরণটি প্রদর্শন করে তিন বছর আগে আমি লিখেছি এমন কিছু কোড এখানে
জোশ হাবদাস

8

আরেকটি বিকল্প হ'ল প্রতিশ্রুতি.আল ব্যবহার করা সমাধানের প্রতিশ্রুতিগুলির অ্যারের জন্য অপেক্ষা করা এবং তারপরে সেগুলি কার্যকর করা।

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

function append_output(suffix, value) {
  $("#output_"+suffix).append(value)
}

function kickOff() {
  let start = new Promise((resolve, reject) => {
    append_output("now", "start")
    resolve("start")
  })
  let middle = new Promise((resolve, reject) => {
    setTimeout(() => {
      append_output("now", " middle")
      resolve(" middle")
    }, 1000)
  })
  let end = new Promise((resolve, reject) => {
    append_output("now", " end")
    resolve(" end")
  })

  Promise.all([start, middle, end]).then(results => {
    results.forEach(
      result => append_output("later", result))
  })
}

kickOff()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Updated during execution: <div id="output_now"></div>
Updated after all have completed: <div id="output_later"></div>


এটিও কাজ করবে না। এটি কেবল প্রতিশ্রুতিগুলি যথাযথভাবে চালিত করে। প্রতিশ্রুতি শেষ হওয়ার আগেই কিকআফ () এর পরে যে কোনও কিছু চলতে চলেছে।
ফিলিপ রেগো

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

বিটিডাব্লু: আপনি এই ঝাঁকুনিটি
স্টান

-1

আপনি নিজে এটি করতে পারেন। (আমি জানি, এটি দুর্দান্ত সমাধান নয়, তবে ..) whileলুপ ব্যবহার করুন যতক্ষণ resultনা এর কোনও মূল্য থাকে না

kickOff().then(function(result) {
   while(true){
       if (result === undefined) continue;
       else {
            $("#output").append(result);
            return;
       }
   }
});

4
অপেক্ষা করার সময় এটি পুরো সিপিইউ গ্রাস করবে।
লুকাসজ গুমিনস্কি

এটি একটি ভয়াবহ সমাধান
জেএসএন

ইয়া তবে এটিই একমাত্র উপায় যা কাজ করে বাসা বাঁধে না বা কাজ করে promises তারপরেও নেস্টেড ফাংশন / প্রতিশ্রুতিগুলির পরে লাইনগুলি শেষ হওয়ার আগেই চলে run
ফিলিপ রেগো

এটি কাজ করবে না। যেহেতু আপনি একটি সীমাহীন লুপটিতে resultপরিবর্তনের জন্য অপেক্ষা করছেন , ফল কখনও কখনও পরিবর্তনের সুযোগ পাবে না কারণ ইভেন্টের লুপটি আর কোনও কিছুতেই প্রক্রিয়া করতে পারে না। এবং, resultএটি একটি ফাংশন আর্গুমেন্ট যা কোনওভাবেই পরিবর্তিত হয় না।
jفر00
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.