مقدمة: لماذا Go وWebAssembly على المستوى المتقدم؟
\n\nتحولت WebAssembly (Wasm) من تقنية تجريبية إلى معيار للتطبيقات عالية الأداء على الويب. Go، بفضل ترجمته الفعالة ودعمه المدمج لـ Wasm، هي واحدة من أفضل اللغات لإنشاء وحدات تعمل على حدود إمكانيات المتصفح. الأمثلة الأساسية مثل "Hello, World!" لم تعد ذات صلة — المهام الحديثة تتطلب تحكمًا دقيقًا في الذاكرة، وتبادلًا ثنائي الاتجاه للبيانات مع JavaScript، وتحسين حجم الملف الثنائي.
\n\nفي هذه المقالة، سنستعرض التقنيات المتقدمة للعمل مع WebAssembly باستخدام Go. ستتعلم كيفية نقل هياكل البيانات المعقدة، واستدعاء دوال Go من JavaScript والعكس، بالإضافة إلى كيفية تحسين أداء وحدات Wasm الخاصة بك. المادة موجهة للمطورين الذين هم على دراية بأساسيات Go ويرغبون في الارتقاء بمهاراتهم إلى المستوى التالي.
\n\n1. إدارة الذاكرة: نقل السلاسل والمصفوفات والهياكل
\n\nإحدى المشكلات الرئيسية عند العمل مع WebAssembly هي نقل البيانات المعقدة بين الذاكرة الخطية لـ Wasm وكومة JavaScript. يستخدم Go جامع القمامة الخاص به، مما يفرض قيودًا. دعنا نستعرض الطرق المتقدمة.
\n\n1.1. نسخ السلاسل باستخدام `copyBytesToGo` و `copyBytesToJS`
\n\nالنقل المباشر للسلاسل عبر معاملات الدوال غير فعال للبيانات كبيرة الحجم. بدلاً من ذلك، استخدم طرق الحزمة syscall/js:
package main\n\nimport (\n "syscall/js"\n "unsafe"\n)\n\n//export processLargeString\nfunc processLargeString(this js.Value, args []js.Value) interface{} {\n if len(args) < 1 {\n return nil\n }\n jsStr := args[0]\n // الحصول على طول السلسلة\n length := jsStr.Get("length").Int()\n // إنشاء مخزن مؤقت في ذاكرة Go\n buf := make([]byte, length)\n // نسخ البيانات من JS إلى Go\n js.CopyBytesToGo(buf, jsStr)\n // الآن buf يحتوي على بايتات UTF-8 للسلسلة\n // ... معالجة ...\n // إرجاع النتيجة\n result := []byte("الرد: " + string(buf))\n dst := js.Global().Get("Uint8Array").New(len(result))\n js.CopyBytesToJS(dst, result)\n return dst\n}\n\nfunc main() {\n c := make(chan struct{}, 0)\n js.Global().Set("processLargeString", js.FuncOf(processLargeString))\n <-c\n}\n\n\n1.2. نقل الهياكل عبر تسلسل JSON
\n\nللهياكل المتداخلة المعقدة، استخدم JSON كتنسيق وسيط. هذا أبطأ من العمل المباشر مع الذاكرة، لكنه أكثر ملاءمة بشكل كبير:
\n\n//export processUserData\nfunc processUserData(this js.Value, args []js.Value) interface{} {\n if len(args) < 1 {\n return js.ValueOf(map[string]interface{}{"error": "no data"})\n }\n // الحصول على سلسلة JSON من JS\n jsonStr := args[0].String()\n var user User\n if err := json.Unmarshal([]byte(jsonStr), &user); err != nil {\n return js.ValueOf(map[string]interface{}{"error": err.Error()})\n }\n // معالجة البيانات\n user.Age++\n // إرجاع JSON\n result, _ := json.Marshal(user)\n return js.ValueOf(string(result))\n}\n\n\n2. الاتصال ثنائي الاتجاه: استدعاء JavaScript من Go والعكس
\n\nالتكامل المتقدم يتطلب أن تكون وحدة Go قادرة ليس فقط على الرد على الاستدعاءات، بل أيضًا على بدء الإجراءات في بيئة JavaScript.
\n\n2.1. تسجيل دوال الاستدعاء (Callbacks) من Go لـ JavaScript
\n\nاستخدم js.FuncOf لإنشاء دوال مرئية من JS. من المهم إدارة دورة حياتها بشكل صحيح:
var callback js.Func\n\nfunc registerCallback() {\n callback = js.FuncOf(func(this js.Value, args []js.Value) interface{} {\n // الحصول على دالة الاستدعاء من JS\n jsCallback := args[0]\n // استدعاؤها مع بيانات من Go\n go func() {\n // محاكاة عمل غير متزامن\n time.Sleep(100 * time.Millisecond)\n jsCallback.Invoke(js.ValueOf("نتيجة من Go"))\n }()\