جاوااسکریپت - Promise.resolve در مقابل new Promise(resolve)


من از bluebird استفاده میکنم و دو راه برای تبدیل توابع همزمان به یک Promise میبینم، اما تفاوتهای بین هر دو روش را نمیفهمم. به نظر میرسد که stacktrace کمی متفاوت است، پس آنها فقط یک alias نیستند، درست است؟

پس روش ترجیحی چیست؟

راه A

function someFunction(someObject) {
  return new Promise(function(resolve) {
    someObject.resolved = true;
    resolve(someObject);
  });
}

راه ب

function someFunction(someObject) {
  someObject.resolved = true;
  return Promise.resolve(someObject);
}

پاسخ شماره ۱

بر خلاف هر دو پاسخ در نظرات - تفاوتی وجود دارد.

در حالی که

Promise.resolve(x);

عملاً همانند

new Promise(function(r){ r(x); });

یک ظرافت وجود دارد.

توابعی که وعده بازمیگردانند به طور کلی باید تضمین داشته باشند که به صورت همزمان خطا ایجاد نمیکنند زیرا ممکن است به صورت ناهمزمان خطا ایجاد کنند. برای جلوگیری از نتایج غیرمنتظره و شرایط مسابقه - اغلب خطاها به رد شدن برگشتی تبدیل میشوند.

با در نظر گرفتن این موضوع - زمانی که مشخصات ایجاد شد، سازندهی وعده ایمن در برابر پرتاب است.

اگر someObject undefined باشد چه؟

  • راه A یک وعده رد شده را برمیگرداند.
  • راه B بهصورت همزمان پرتاب میکند.

بلوبرد این را دید و پتکا Promise.method را اضافه کرد تا این مشکل را برطرف کند، بنابراین شما میتوانید به استفاده از مقادیر بازگشتی ادامه دهید. بنابراین روش صحیح و آسان برای نوشتن این در بلوبرد در واقع هیچکدام نیست - آن این است:

var someFunction = Promise.method(function someFunction(someObject){
    someObject.resolved = true;
    return someObject;
});

متد Promise.method خطاهای پرتاب شده را به reject و مقادیر بازگشتی را به resolve تبدیل میکند. این امنترین روش برای انجام این کار است و مقادیر بازگشتی را به صورت thenable در نظر میگیرد، بنابراین حتی اگر someObject در واقع خود یک پرامیس باشد، کار خواهد کرد.

به طور کلی، Promise.resolve برای تبدیل اشیاء و وعدههای خارجی (thenables) به وعدهها استفاده میشود. این مورد استفاده آن است.

پاسخ شماره ۲

تفاوت دیگری وجود دارد که در پاسخها یا نظرات فوق ذکر نشده است:

اگر someObject یک Promise باشد، new Promise(resolve) دو تیک اضافی هزینه خواهد داشت.

const p = new Promise(resovle => setTimeout(resovle));

new Promise(resolve => resolve(p)).then(() => {
  console.log("tick 3");
});

p.then(() => {
  console.log("tick 1");
}).then(() => {
  console.log("tick 2");
});

const p = new Promise(resolve => setTimeout(resolve));

Promise.resolve(p).then(() => {
  console.log("tick 3");
});

p.then(() => {
  console.log("tick 1");
}).then(() => {
  console.log("tick 2");
});

قطعه دوم ابتدا 'tick 3' را چاپ میکند. چرا؟

  • اگر مقدار یک پرامیس باشد، Promise.resolve(value) دقیقا همان مقدار را برمیگرداند. Promise.resolve(value) === value درست خواهد بود. به MDN مراجعه کنید

  • اما new Promise(resolve => resolve(value)) یک پرامیس جدید بازمیگرداند که برای دنبال کردن پرامیس value قفل شده است. برای انجام این 'قفل شدن' به یک تیک اضافی نیاز دارد.

      // something like:
      addToMicroTaskQueue(() => {
        p.then(() => {
          /* resolve newly promise */
        })
          // all subsequent .then on newly promise go on from here
          .then(() => {
            console.log("tick 3");
          });
      });
    

    فراخوانی tick 1 .then ابتدا اجرا میشود.

  • http://exploringjs.com/es6/ch_promises.html#sec_demo-promise