Async: تعامل مع الوظائف بدون رد على أنها وعود

تم إنشاؤها على ٩ يوليو ٢٠١٩  ·  12تعليقات  ·  مصدر: caolan/async

السلوك الحالي مثير للدهشة للغاية:

await async.parallel({foo: async()=>Promise.all([1,2])})
// { foo: [ 1, 2 ] }
await async.parallel({foo: ()=>Promise.all([1,2])})
// waits forever because non-async function expects a ballback

لأنه بالنسبة للوظائف القصيرة ، من المغري كتابة foo: () => doSomething() . من السهل جدًا نسيان الكلمة الرئيسية async هناك

لكن هذا لن ينجح حاليًا ، يجب كتابته كـ foo: async () => doSomething()

هل يمكننا التحقق مما إذا تم تمرير رد الاتصال أم لا ، بدلاً من التحقق مما إذا كانت الوظيفة دالة AsyncFunction؟

wont fix

التعليق الأكثر فائدة

حتى مع إغلاق هذه المشكلة ، اعتبارًا من المشكلة المرتبطة أعلاه ، أود إعادة التأكيد على بيانات caub هنا.
لقد أربكني السلوك الحالي كثيرًا ، وبينما أفهم (نوعًا ما) القيود الفنية التي تمت مناقشتها هنا ، أعتقد أن التوثيق ببساطة لا يتماشى مع السلوك الحالي.
على سبيل المثال ، mapLimit تقول "إرجاع: وعد ، إذا لم يتم تمرير أي رد" - لقد بدأت بإصدار رد الاتصال ، ثم تركت رد الاتصال وفوجئت أنني لم أحصل على الوعد (لأنني لم أحصل على الكلمة الأساسية "غير المتزامن").
أيضًا ، ليست هناك حاجة إلى عبارة "callback" في وظيفة iteratee ، فبدلاً من ذلك تحتاج إلى "إرجاع" قيمة.
لذلك ، إما يجب تغيير السلوك (مفضل) أو على الأقل يجب مواءمة التوثيق والأمثلة في وجهة نظري لتجنب الالتباس.

ال 12 كومينتر

ليس لدينا طريقة للقدرة على الكشف بشكل موثوق عما إذا كانت الوظيفة ترجع بوعدًا أم لا ، وما إذا كنا بحاجة إلى إعادة الاتصال بها أم لا مسبقًا. ( Function.length أو تحليل Function.toString() ليس جيدًا بما يكفي) هذا ما يدور حوله asyncify - تحويل وظائف إرجاع الوعد إلى وظائف تتطلب رد الاتصال. نظرًا لأنه يمكننا اكتشاف وظائف async نظرًا لأن لديهم نوعًا مختلفًا ، يمكننا تلقائيًا asyncify لهم (وهذه هي الطريقة التي يعمل بها هذا داخليًا). ولكن بالنسبة للوظائف العادية التي تحدث لتعود بوعود ، فليس لدينا طريقة لمعرفة ما قبل أن يستدعيها Async.

aearly حسنًا ، حسنًا ، ربما شيء أبسط مثل اكتشاف رد الاتصال في المستوى الأعلى؟

async function parallel(o, cb) {
  if (!cb) { // promise behavior
    return Object.fromEntries(
      await Promise.all(Object.entries(o).map(async ([k, v]) => [k, await v()]))
    )
  }
  // else do the callback behavior
}

لقد فعلنا شيئًا من هذا القبيل - نعيد وعدًا إذا حذفت رد الاتصال إلى async.parallel . نحن لا نفترض أن الوظائف التي تمررها إليه تعيد الوعود إذا حذفت رد الاتصال إما لأنه ليس لدينا طريقة لتحمل نية المستخدم. ربما أنا أسيء فهم ما تعنيه؟

حسنًا ، لنكن ملموسين: https://repl.it/@caub/async -async

// npm.im/async expects all inner functions to be async,
// even when using no outer callback (as 2nd arg of async.parallel)
console.log(
  await async.parallel({
    foo: async () => delay(1000).then(() => 1),
    bar: async (rs) => {
      return 2
    },
  })
);

// Doesn't work without those async,
// but I'd like this to resolve just like above instead of freezing
console.log(
  await async.parallel({
    foo: () => delay(1000).then(() => 1),
    bar: () => {
      return 2
    },
  })
);

أعتقد ، في إصدار رئيسي تالٍ مستقبلي ، قد يكون من المثير للاهتمام دعم رد الاتصال فقط بطريقة مشابهة لما يلي:

await Promise.all([
  { then(resolve){ setTimeout(resolve, 50, 1) } }, // callback way
  2,
  delay(45).then(())=>3)
])
// [1, 2, 3]

إزالة الحاجة إلى الكلمات الرئيسية غير الضرورية async في الوظائف الداخلية

الميزة هي المزيد من الوضوح. أتفهم وجهة نظرك حول عدم افتراض نية المستخدم ، ولكنك لا تزال تفترض أن الأشخاص لن ينسوا الكلمة الرئيسية async عند الرغبة في استخدام حل الوعد

سيؤدي ذلك إلى كسر التوافق العكسي بطريقة ضخمة جدًا - على سبيل المثال ، لم يعد بإمكانك القيام بأشياء مثل Async.map(fileNames, fs.readFile) .

هل يمكن أن تشرح أين يمكن أن تنكسر؟

ما كسر حاليا هو:

await async.map(['server.js', 'package.json'], fs.readFile) // works
await async.map(['server.js', 'package.json'], fs.promises.readFile) // works
await async.map(['server.js', 'package.json'], s => fs.promises.readFile(s)) // doesn't work
await async.map(['server.js', 'package.json'], async s => fs.promises.readFile(s)) // works

الفكرة هي إصلاح الثالث

أعاني من هذه المشاكل باستمرار ، على سبيل المثال كتبت:

  // ...
  doc = await async.map(items, item => item.toDoc(currentUser, options));

في مكان ما ، ومرة ​​أخرى ، انتهت المهلة لأنني كنت أفتقد async item => item.toDoc..

إذا كنت وحدي في هذه الحالة ، فهذا لا يستحق ، ولكن إذا كان هناك المزيد من الأشخاص في هذا الموقف ، أعتقد أنه من المفيد التفكير في التغيير المقترح

هذا المثال مع async.map هو أيضًا تمثيلي للغاية ، حيث يمكنك رؤية وجهة نظري مع الكيفية

  doc = await Promise.all(items.map(item => item.toDoc(currentUser, options)));

يعمل بدون الحاجة إلى async item => item.toDoc..

لا توجد طريقة لإصلاح الثالث بشكل موثوق ، فقد نوقش هذا كثيرًا في الماضي. لا يمكننا التحقق من القيمة المعادة لوظيفة ما ثم بأثر رجعي لا نمرر رد نداء إلى الوظيفة. الكلمة الرئيسية async جيدة كما هي.

caub Shameless plug هنا ، لكنني أعتقد أن asyncp لديه القدرة على التعامل مع ما تبحث عنه هنا.

حتى مع إغلاق هذه المشكلة ، اعتبارًا من المشكلة المرتبطة أعلاه ، أود إعادة التأكيد على بيانات caub هنا.
لقد أربكني السلوك الحالي كثيرًا ، وبينما أفهم (نوعًا ما) القيود الفنية التي تمت مناقشتها هنا ، أعتقد أن التوثيق ببساطة لا يتماشى مع السلوك الحالي.
على سبيل المثال ، mapLimit تقول "إرجاع: وعد ، إذا لم يتم تمرير أي رد" - لقد بدأت بإصدار رد الاتصال ، ثم تركت رد الاتصال وفوجئت أنني لم أحصل على الوعد (لأنني لم أحصل على الكلمة الأساسية "غير المتزامن").
أيضًا ، ليست هناك حاجة إلى عبارة "callback" في وظيفة iteratee ، فبدلاً من ذلك تحتاج إلى "إرجاع" قيمة.
لذلك ، إما يجب تغيير السلوك (مفضل) أو على الأقل يجب مواءمة التوثيق والأمثلة في وجهة نظري لتجنب الالتباس.

caub هذا هو السبب
https://github.com/caolan/async/blob/8aecf108b3922bc5211036706a0f6f75e02bd42b/lib/internal/wrapAsync.js#L3 : L5

function isAsync(fn) {
    return fn[Symbol.toStringTag] === 'AsyncFunction';
}

بهذه الطريقة ، يكون من الأسهل التحقق مما إذا كانت الوظيفة ترجع وعدًا دون الاتصال به فعليًا. لكن ما زلت أتفق معك على أن هذا ضد الاتفاقيات غير المتزامنة / المنتظرة. نظرًا لأن إضافة كلمة رئيسية async بدون await داخلية تعتبر ممارسة سيئة في معظم الحالات.

من https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

يمكن اعتبار جسم الدالة async مقسومًا على صفر أو أكثر من التعبيرات المنتظرة. يتم تشغيل رمز المستوى الأعلى ، بما في ذلك أول تعبير انتظار (إذا كان هناك واحد) ، بشكل متزامن.

... وهكذا سيكون عديم الفائدة والأسوأ أنه سيضيف بعض النفقات العامة للأداء إذا تم نقل الكود بواسطة babel على سبيل المثال لأنه سيضيف المزيد من التعليمات البرمجية والخطوات فقط بسبب وجود الكلمة الرئيسية async

على سبيل المثال ، ما يلي:

async function foo() {
   return 1
}

.. يعادل:

function foo() {
   return Promise.resolve(1)
}

لكن مع هذه المكتبة ،

async.mapLimit([1], 1, async v => v)

و

async.mapLimit([1], 1, v => Promise.resolve(v)))

ليست مكافئة!

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات