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