Posts Tagged ‘html5’

محاضرة The end of all things

10/04/2010

السلام عليكم و رحمة الله تعالى و بركاته

أعطى دوجلاس كروكفور يوم 31/3/2010 محاضرة بعنوان The end of all things و بهذا أنهى سلسلتة Corckford on JavaScript ، يمكنك تحميل المحاضرة عالية الجودة من هنا 705 ميجا بايت ، و من هنا ستجد خيارات اخرى للتحميل و الاجزاء السابقة من السلسلة .

HTML5 storage الجزء الثانى

14/08/2009

السلام عليكم و رحمه الله تعالى و بركاته

فى هذا الجزء أتناول كيفيه تخزين البيانات فى قاعده بيانات SQLite من خلال local JavaScript Database api

مقدمه
إنشاء و فتح قاعده البيانات
إنشاء جدول جديد
إضافه بيانات للجدول
إستخدام العبارات الجاهزه
إختيار بيانات من الجدول
التعامل مع اخطاء تنفيذ ال SQL
أرقام الأخطاء و مدلولاتها
التعامل مع اخطاء ال transaction
التعامل مع اصدارات قاعده البيانات
الدعم من المتصفحات
للمزيد

مقدمه

تحتوى مواصفات HTML5 على وصف ل Local JavaScript Database قاعده بيانات محليه تمكنك من تخزين البيانات داخل متصفح المستخدم فى قاعده بيانات SQlite بدلا من الكوكيز التى تقتصر مساحتها التخزينيه على 4 كيلو فقط ، قاعده البيانات SQLite على ملايين اجهزه المحمول و الاجهزه الكفيه و السيرفر و الكثير من تطبيقات سطح المكتب لصغر حجمها و مرونتها و تدعيمها شبه الكامل ل SQL ANSI ، للمزيد عن SQL و SQLite داخل المتصفح من هنا ، العمليات التى تتم على قاعده البيانات تكون غير تزامنيه asynchronous اى تتطلب كل عمليه تسجيل وظيفه تستمع لحدث نجاح العمليه او حدوث خطأ ، كما ان العمليات كلها تتم بنظام transaction لأن بيئه المتصفح غير مستقره ، يمكن لأى عمليه ان يقطعها refresh او اغلاق نافذه المتصفح ، اى انه إذا حدث خطأ فى عمليه مكونه من عده خطوات فإن قاعده البيانات لن تتأثر بأى من هذه الخطوات ، مثلا اذا كنت تغير بعض البيانات من جدول A ثم تضيف بيانات فى جدول B و تحذف بيانات من جدول C و قمت بإغلاق المتصفح فى منتصف خطوه اضافه البيانات للجدول B ، فإن خطوه تغيير البيانات من الجدول A سيتم التراجع عنها و لن يحدث اى تأثير ، إلا اذا تمت جميع عمليات ال transaction بنجاح .

إنشاء و فتح قاعده بيانات

قبل ان تتمكن من إستخدام قاعده البيانات عليك اولا فتح اتصال بها ، عندما تقوم بفتح اتصال سيتم انشاء قاعده بيانات جديده فارغه اذا لم يتم انشائها من قبل ، لذلك فإن عمليه فتح و انشاء قاعده البيانات متماثله ، يتم فتح الاتصال بقاعده البيناات عن طريق عن طريق الوظيفه openDatabase التى تقبل اربع عبارات : العباره الاولى هى إسم قاعده البيانات ، العباره التانيه هى إصدار قاعده البيانات للمزيد انظر الجزء الخاص بالتعامل مع اصدارات قاعد البيانات ، العباره الثالثه هى الإسم الذى يستخدمه المتصفح للتفاعل مع المستخدم مثلا عند طلب إذن بزياده الحجم المتاح لقاعده البيانات ، العباره الرابعه هى الحجم الذى تتوقع ان تشغله قاعده البيانات التى تقوم بالعمل عليها و اذا تعدت هذا الحجم المتفق عليه سيتم أخذ إذن المستخدم بأعطاء المزيد من الحجم لقاعده البيانات ، المثال التالى يوضح إنشاء قاعده بيانات بإسم keepondev إصدار 1.0 و حجمها 64 كيلوبايت و لها الإسم keepondev database لإستخدامه عند التفاعل مع المستخدم :

var db = openDatabase("keepondev", "1.0", "Keepondev database", 65536);

إنشاء جدول جديد

بقيه امثله هذا الموضوع تعتمد على جدول بإسم goals يحتوى على عمودين id و name :

CREATE TABLE IF NOT EXISTS goals (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL
);

يمكنك انشاء هذا الجدول من خلال تنفيذ جمله ال SQL السابقه على قاعده البيانات keepondev عن طريق الوظيفه executeSql التابعه للكائن SQLtransaction – فى المثال بإسم tx – الذى يتم تمريره للوظيفه التى تمرر للوظيفه transaction 8O التابعه للكائن الناتج من openDatabase كما يوضح الكود التالى :

var db = openDatabase("keepondev", "1.0", "Keepondev database", 65536);

function resultHandler(){
    alert("success");
};
function errorHandler(){
    alert("fail");
};
db.transaction(function(tx){
    var sql = "CREATE TABLE IF NOT EXISTS goals(";
    sql += "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
    sql += "name TEXT NOT NULL DEFAULT 'secret goal');";
    tx.executeSql(sql, [], resultHandler, errorHandler);
});

الوظيفه transaction تقبل وظيفه يمرر لها كائن من نوع SQLTransaction بإسم tx فى المثال السابق ، الكائن tx يحتوى الوظيفه executeSql التى تقبل اربع عبارات ، العباره الاولى هى جمله ال SQL التى قمت بوضعها فى المتغير sql و كتبتها على اكثر من سطر حتى لا تخرج عن عرض المدونه ، العباره التانيه مصفوفه تقبل قيم العبارت الجاهزه للمزيد انظر الجزء الخاص بإستخدام العبارات الجاهزه prepared statements ، العباره الثالثه وظيفه يتم إستدعائها اذا تم تنفيذ جمله ال SQL بنجاح ، و العباره الرابعه وظيفه يتم إستدعائها اذا حدث خطأ اثناء تنفيذ جمله ال SQL ، تلك الوظيفتين ستقوم بإظهار نافذه منبثقه ب success او fail فقط ، للمزيد انظر الجزء الخاص بأخطاء تنفيذ ال SQL و الجزء الخاص بأخطاء ال transaction ، يمكنك التأكد من انشاء الجدول و قاعده البيانات بنجاح داخل المتصفح Safari 4 من خلال فتح ال tab الخاص ب database الذى يمكن الحصول عليه من Sesstings > preferences > advanced > show develop menu in menu bar ، ستجد قائمه جديده بإسم develop دخلت فى القائمه file ، قم بقتح tab ال database كما توضح الصوره التاليه :

Safari Database tab

داخل tab ال database ستجد قاعده بيانات بإسم keepondev تحتوى على جدول فارغ بإسم goals ، يالا بينا نملاه .

إضافه بيانات للجدول

إضافه بيانات للجدول ماهى إلا تنفيذ جمله SQL من نوع INSERT بعد فتح قاعده البيانات و عمل transaction بنفس ترتيب الخطوات السابقه ، كما يوضح المثال البسيط التالى :

var db = openDatabase("keepondev", "1.0", "Keepondev database", 65536);
db.transaction(function(tx){
    var sql = "INSERT INTO goals (name) VALUES ('world domination')";
    tx.executeSql(sql, [], resultHandler, errorHandler);
});

الشىء الوحيد المختلف فى المثال السابق هى جمله ال INSERT التى تقوم بإضافه صف جديد فى الجدول goals و تظبط قيمه العمود name الى world domination اما العمود id فيأخذ رقم تلقائى ، هناك مأخذ على طريقه الاضافه السابقه : لو احتوت القيمه على ‘ سيحدث error و يمكن ايضا حقن جمله SQL و عمل هجمه من نوع SQL injection ، الطريقه الافضل لإضافه البيانات هى إستخدام العبارت الجاهزه .

إستخدام العبارات الجاهزه

إستخدام العباره الجاهزه يمكنك من إستخدام ال ‘ و ال ” داخل القيم بدون الخوف على كسر جمله ال SQL ، ثم انه يستحيل حقن SQL داخل العبارت الجاهزه و عليه لا يمكن حدوث هجمات SQL injection ، و تزيد كفائه تنفيذ عمليه اضافه اكثر من صف فى الجدول .

قم بوضع علامه استفهام ؟ بدلا من وضع قيمه العمود فى جمله ال SQL ، و بداخل المصفوفه [] التى هى العباره التانيه للوظيفه executeSql قم بإضافه القيمه ، او ضيفها من ناتج مدخل المستخدم كما يوضح الكود البسيط التالى :

var db = openDatabase("keepondev", "1.0", "Keepondev database", 65536);
db.transaction(function(tx){
    var sql = "INSERT INTO goals (name) VALUES (?)";
    tx.executeSql(sql, ["Conference speaker"], resultHandler, errorHandler);
    tx.executeSql(sql, ["technology innovator"], resultHandler, errorHandler);
    tx.executeSql(sql, ["JavaScript evangelist"], resultHandler, errorHandler);
    tx.executeSql(sql, ["most infeluence person of all time"], resultHandler
    , errorhandler);
    tx.executeSql(sql, ["space trip"], resultHandler, errorhandler);
});

يمكنك منع التكرار فى الكود بإستخدام partial function و النتيجه كما تظهر فى الصوره التاليه :

database tab showing keepondev goals table

إختيار بيانات من الجدول

اختيار بيانات من الجدول عباره عن استخدام جمله SQL من نوع SELECT بالإضافه إلى إستقبال البيانات من خلال الوظيفه resultHandler التى كانت وظيفتها فى الامثله السابقه عمل alert بالنص Success فقط ، عند نجاح تنفيذ جمله ال SQL على قاعده البيانات ، يتم تمرير عبارتين للوظيفه resultHandler ، العباره الاولى هى كائن من نوع SQLtTansaction و العباره التانيه كائن من نوع SQLResultSet يحتوى على الصفوف الناتجه من تنفيذ جمله ال SQL ، حيث يحتوى على الخاصيه rows التى هى بمثابه مصقوفه تمثل صفوف الجدول التى رجعت ، يمكننا استخلاص البيانات منها كما يوضح المثال البسيط التالى :

function resultHandler(tx, result){
    var rows = result.rows;
    var rowsLen = rows.length;
    for(var i = 0; i < rowsLen; i += 1){
        var row = rows.item(i);
        var id = row["id"];
        var name = row["name"];
        alert(id + " : " + name);
    }
};

var db = openDatabase("keepondev", "1.0", "Keepondev database", 65536);
db.transaction(function(tx){
    var sql = "SELECT * FROM goals";
    tx.executeSql(sql, [], resultHandler, errorHandler);
});

فى المثال السابق قمت بإستخلاص جميع البيانات من الجدول goals و استقبلت الناتج داخل الوظيفه resultHandler و قمت بعمل alert للصف لكن يمكنك حقنه فى ال DOM و عمل pagination و عمل LIMIT داخل جمله ال SQL حتى لا تتأثر كفائه التطبيق .

إذا كانت الجمله السابقه INSERT يمكنك معرفه ال id الجديد للصف من خلال result.insertId و عدد الصفوف التى تأثرت من result.rowsModified من داخل resultHandler .

التعامل مع اخطاء تنفيذ ال SQL

يتم إستدعاء الوظيفه errorHandler عند حدوث خطأ فى تنفيذ جمله ال SQL مع تمرير عبارتين لها ، العباره الاولى هى كائن من نوع SQLTransaction ، و العباره التانيه كائن من نوع SQLError يحتوى على معلومات عن الخطأ الذى حدث ، الوظيفه errorHandler اذا قامت بإرجاع true سيتم الغاء جميع العمليات فى ال transaction الحالى ، و اذا قامت ب return false سيتم التغاضى عن الخطأ و لن يتم الغاء ال transaction ، الكائن SQLError يحتوى على خاصيتين ، الخاصيه الاولى message و هى رساله توضح الخطأ الذى حدث يمكنك استخدامها فى ال debugging ، اما الخاصيه التانيه هى code فهى رقم يدل على الخطأ الذى حدث ، انظر الجزء الخاص بأرقام الاخطاء .

function errorHandler(tx, error){
    alert(error.message); // unrecognized token: 3awzo
    alert(error.code); // 1
};
db.transaction(function(tx){
    var sql = "SELECT elly ana 3awzo yabni";
    tx.executeSql(sql, [], resultHandler, errorHandler);
});

أرقام الاخطاء و مدلولاتها

داخل الوظيفه errorHandler ناتج error.code رقم يوضح طبيعه الخطأ ، ال 0 يوضح ان الخطأ غير متعلق بقاعده البيانات ، و 1 يوضح ان الخطأ له علاقه بقاعده البيانات ، 2 يوضح ان الخطأ اصدار قاعده البيانات ليس اصدار القاعده التى طلبتها للمزيد انظر الجزء الخاص بإصدارات قاعده البيانات ، و 3 يوضح ان نتيجه جمله ال SQL كبيره جدا و فى هذه الحاله عليك إستخدام LIMIT و OFFSET حتى تحدد عد الصفوف الناتجه من جمله ال SQL ، و 4 توضح ان الحجم المتاح لقاعده البيانات قد تعدى و ان المستخدم رفض اعطاء المزيد من الحجم عند سؤال المتصفح له ، و 5 يدل على ان الخطأ من نوع lock contention error و عليك اعاده تنفيذ ال transaction ، و 6 يل على constraint error مثلا عند تكرار قيمه فى صفين لعمود معلم ك UNIQUE او عدم اعطاء قيمه لعمود NOT NULL .

التعامل مع اخطاء ال transaction

كما تمكننا من التعامل مع اخطاء تنفيذ جمله ال SQL ، يمكنك ايضا اتخاذ اجراءات عند وقوع خطأ فى تنفيذ ال transaction الذى قد يحتوى على تنفيذ عده جمل SQL ، الوظيفه transaction تقبل ثلاث عبارات ، فى الامثله السابقه كلها رأينا العباره الاولى فقط و هى عباره عن وظيفه يمرر لها الكائن SQLTransaction ، اما العباره الثانيه التى تمرر للوظيفه transaction هى وظيفه يتم استدعائها عند حدوث خطأ فى ال transaction و العباره الثالثه يتم استدعائها عند نجاح ال transaction كما يوضح المثال البسيط التالى :

function txError(error){
    alert(error.message);
    alert(error.code);
};
function txSuccess(){
    alert("transaction succeed");
};

db.transaction(function(tx){ 
    // execute some sql queries here
}, txError, txSuccess);

فى المثال السابق سيتم استدعاء txSuccess اذا نجح ال transaction بدوت اى اخطاء ، و سيتم استدعاء الوظيفه txError مع تمرير كائن SQLError لها ، يمكن استخلاص بيانات منها من خلال error.message او error.code للمزيد انظر أرقام الاخطاء و مدلولاتها .

التعامل مع اصدارات قاعده البيانات

الوظيفه openDatabase التى تقوم بفتح قاعده البيانات تقبل العباره التانيه لها إصدار قاعده البيانات ، اذا قمت بتحديد اصدار خطأ سيتم استدعاء الوظيفه المرتبطه بحدوث الخطأ ، انظر الجزء الخاص بأرقام الاخطاء و مدلولاتها ، و اذا اعطيت اصدار القاعده عباره عن نص فارغ ، سيتم فتح قاعده البيانات بغض النظر عن إصدارها كما يوضح الكود البسيط التالى :

var db = openDatabase("keepondev", "", "Keepondev database", 65536);

يمكنك معرفه اصدار قاعده البيانات بعد ذلك من خلال الخاصيه verison كما يلى

alert(db.version); // 1.0

الكود السابق نتج عنه 1.0 لأنه لا يوجد إصدارات من قاعده بيانات بإسم keepondev إلا الاصدار 1.0 الذى قمنا بإنشاءه و اضافه فيه بيانات من قبل .

يمكنك ترقيه قاعده البيانات إلى اصدار احدث مع الحفاظ على البيانات بداخها كما هى او تغيرها من خلال الوظيفه changeVersion التى تقبل اربع عبارات ، العباره الاولى هى اصدار قاعده البيانات التى تريد تغيرها ، العباره التانيه هى الاصدار الجديد ، العباره التالته هى الوظيفه التى تقوم بتغير الجداول او تغير ال schema و تقبل كائن من نوع SQLTransaction ، العباره الرابعه هى الوظيفه التى يتم استدعائها اذا حدث خطأ اثناء الترقيه مع تمرير الكائن SQLError لها ، اما العباره الخامسه و الاخيره هى الوظيفه التى يتم استدعائها اذا نجحت عمليه الترقيه ، كما يوضح المثال البسيط التالى :

function upgrade(tx){
    var sql = "ALTER TABLE goals RENAME TO targets";
    tx.executeSql(sql, [], resultHandler, errorHandler);
};
function upgradeError(error){
    alert("upgrade error :" + error.message);
};
function upgradeSuccess(){
    alert("upgrade completed");
}
db.changeVersion("1.0", "2.0", upgrade, upgradeError, upgradeSuccess);

فى المثال السابق تم ترقيه قاعده البيانات إلى الاصدار 2.0 مع تغيير إسم الجدول goals إلى targets مع الاحتفاظ بالبيانات كما هى كما توضح الصوره التاليه :

keepondev database version 2.0 and goals table converted to targets

الدعم من المتصفحات

وقت كتابه التدوينه ال local javascript database api مدعم فى safari 3.1+ و iphone os 2.0+ و المتصفحات الاخرى قريبا ان شاء الله .

للمزيد

ADC > using JavaScript Databases

HTML5 storage الجزء الاول

13/07/2009

السلام عليكم و رحمه الله تعالى و بركاته

فى هذا الجزء أتناول الكائن sessionStorage لتخزين البيانات بين الصفحات ، و الكائن localStorage لتخزين البيانات بين الصفحات و حتى بعد اغلاق المتصفح ، و الكائن globalStorage الذى تم الغاءه من مواصفات HTML5 لكنه لازال متواجد فى FireFox2 .

مقدمه
الكائن sessionStorage
الكائن localStorage
الكائن globalStorage
الحدث storage
الدعم من المتصفحات
المزيد

مقدمه

تحتوى HTML5 على الكثير من الكائنات لتخزين البيانات فى المتصفح بدلا من ال cookies التى تقتصر مساحتها التخزينيه على 4Kb و غير امنه و استخراج البيانات منها يتطلب اعراب ال النص المخزن ، و ذلك لتوفير وسيله اكثر امانا و ضخمه لتخزين ازواج البيانات key/value pairs لإستخدامها لاحقا اما بين الصفحات بإستخدام sessionStorage او بين الصفحات و حتى بعد اغلاق المتصفخ و فتحه بإستخدام الكائن localStorage ، او تخزين البيانات فى قواعد بيانات SQLite كما سنرى الجزء فى الثانى ان شاء الله ، او تخزين ملفات التطبيق مثل js,css,html,jpg … الخ حتى يعمل التطبيق اوفلاين عند انقطاع الانترنت و سرعه بدأ التطبيق عندما يرجع اتصال النترنت لأن ملفات التطبيق كلها على جهاز المستخدم يخزنها المتصفح ، ملفات السيرفر فقط مثل php مخزنه على السيرفر كما سنرى فى الاجزاء اللاحقه ان شاء الله .

sessionStorage

الكائن sessionStorage يقوم بتخزين البيانات فى جلسه الصفحه page session التى تبدأ منذ بدايه فتح الصفحه و حتى اغلاق النافذه او ال tab ، و بهذا يمكنك تخزين و الوصول إلى البيانات بين الصفح المختلفه مادام فى نفس ال tab او النافذه و تحت نفس الدومين ، و يتم مسح هذه البيانات فور اغلاق ال tab او النافذه و لا يمكنك الوصول للبيانات عند فتح هذه الصفحه مره اخرى ، يمكن تخزين و الوصول للبيانات من sessionStorage عن طريق الوظيفتين setItem و getItem كما يوضح المثال البسيط التالى :

// set key/values in this page
sessionStorage.setItem("name", "Mostafa Farghaly");
sessionStorage.setItem("age", 22);
// get values in another page
alert(sessionStorage.getItem("name")); // Mostafa Farghaly
alert(sessionStorage.getItem("age")); //22

ملحوظه : قم بتجربه ال sessionStorage من ال localhost ، لأن التجربه من داخل مسار عادى سينتج عنه خطأ .

او مباشره بدون وظائف عن طريق كتابه اسم ال key كما يوضح الكود البسيط التالى :

sessionStorage.name = "Mostafa Farghaly";
alert(sessionStorage.name); // Mostafa Farghaly

يحتوى الكائن sessionStorage على الخاصيه length التى بها عدد القيم المخزنه فى ال sessionStorage للدومين الحالى :

alert(sessionStorage.length); //2

و يحتوى ايضا على الوظيفه key التى تقبل رقم و ترجع اسم ال key المرتبط به :

alert(sessionStorage.key(0)); //name
alert(sessionStorage.key(1)); //age

و يحتوى ايضا على الوظيفه removeItem التى تقبل key و تقوم بمحو ال key/value بأكمله و تقوم بتقليل ال length واحد 1 كما يلى :

sessionStorage.removeItem("name");
alert(sessionStorage.name); //null

و يحتوى على الوظيفه clear التى تقوم بمحو جميع ال key/value .

الكائن sessionStorage و localStorage مشتقين من النوع Storage :

alert(sessionStorage instanceof Storage); //true

localStorage

الكائن localStorage يطابق interface الكائن sessionStorage و عليه فإنه يحتوى على نفسه خصائصه و وظائفه ، المهم هو ان طريقه عمله تختلف ، حيث يمكنه تخزين و الوصول للبيانات فى صوره ازواج key/value من بدايه كتابتها إلى مسحها ، القيم تظل محفوظه عند التنقل بين الصفحات لنفس الدومين حتى بعد اغلاق النافذه او حتى اعاده تشغيل المتصفح او الكمبيوتر لنفس الدومين .

// set key/value in localStorage
localStorage.goal = "world domination";
//close the page, open it
alert(localStorage.goal); //world domination

globalStorage

firefox2 قام بإدراج الكائن globalStorage الذى تم محوه من مواصفات HTML5 بعد اصدار Firefox2 ، الكائن globalStorage له أيضا نفس ال interface الخاص ب sessionStorage و localStorage لكنه يعمل تماما مثل localStorage ، و قد تم محوه من المواصفات و تقديم localStorage ، يقوم ال globalStorage بتخزين القيم على مستوى الدومين كما يلى :

globalStorage['www.keepondev.com'].name = "Mostafa Farghaly";
alert(globalStorage['www.keepondev.com'].name); //Mostafa Farghaly

يمكنك استخدام الوظيفه التاليه للحصول على globalStorage او localStorage فى حاله تواجد احداهما مع اعطاء الاولويه للتانى :

function getStorage(){
    return (localStorage) ? localStorage : globalStorage[location.hostname];
}

var storage = getStorage();
storage.whyProgramme = "for fun";

ال localStorage مقيد بال host+schema+port لذلك http://www.keepondev.com لا يمكنه قراءه بيانات https://www.keepondev.com لتغير ال schema من http إلى https ، اما ال globalStorage فمقيد بال host فقط ، لذلك الاول اكثر امانا .

الحدث storage

يمكن الاستماع الى الحدث storage الذى يتولد عند كتابه او تعديل او محو اى key/value للكائن localStorage او sessionStorage ، يتم تمرير كائن الحدث storage event إلى الوظيفه التى تستمع للحدث ، و يحتوى على الكثير من الخصائص التى تعرفك المزيد عن الحدث مثل اى key تم تغيره او اضافته ، ماهى القيمه الجديده لهذا ال key … الخ كما يوضح المثال البسيط التالى :


document.body.addEventListener("storage", function(storageEvent){
    alert("the modified key's " + storageEvent.key); //name
    alert("the new value's " + storageEvent.newValue); //John Resig
    alert("the old value was " + storageEvent.oldValue); //Mostafa Farghaly
    alert("the page that made the change's " + storageEvent.url);
    // keepondev.com
    alert("the window where change was made's " storageEvent.source);
    // this [window object]
});

sessionStorage.name = "John Resig"; // fires storage event and call the listening function

الصفحات التى تحت نفس الدومين الواحد فقط هى التى تستطيع الاستماع للحدث storage و الوصول لخصائص كائن الحدث storage .

browser support

وقت كتابه هذه التدوينه :
الكائنات localStorage و sessionStorage مدعم فى FireFox3+, IE8, Safari4
الكائن globalStorage موجود فى Firefox2
انترنت اكسبلورر يحتوى على userDate behaviour لهه وظائف مماثله لتخزين البيانات .

للمزيد
cookies
MDC DOM Storage
IE userData
Flash local Storage
Apple developer connection key-value storage

JavaScript threading

29/06/2009

السلام عليكم و رحمه الله تعالى و بركاته

مقدمه
انشاء Worker جديد
انهاء عمل worker قديم
بعض القيود المفروضه على worker
الكائن navigator
إستيراد الملفات من داخل ال worker
التعامل مع أخطاء ال worker

مقدمه

تطبيقات الجافاسكربت ذات طبيعه single threaded اى ان التطبيق كله يعمل داخل thread واحد فقط فى ال CPU ، يتم تنفيذ اوامر التطبيق بنظام queue حيث يبدأ تنفيذ السطر الاول ثم الثانى حتى ينتهى التطبيق عند السطر الاخير ، و هذا يؤدى الى ان التطبيقات التى تستهلك كميه كبيره من الذاكره – نتيجه اجراء الكثير من العمليات و الحسابات المعقده -ينتهى بها المطاف إلى تجميد واجهه المستخدم UI freezing او انتهاء التطبيق .
خذ عندك مثلا هذه الوظيفه التى تقوم بحساب fibonacci number ، رقم فيبوناتشى هو مجموع رقمى فيبوناتشى للرقمين الذين يسبقوه مع العلم ان رقم فيبوناتشى ل 0 يساوى 0 ، و رقم فيبوناتشى ل 1 = 1 :

function fibonacci(n){
    if(n==0 || n==1){
        return n;
    } else {
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
}

إذا قمت بمحاوله إيجاد رقم فيبوناتشى للرقم 11 فإن الوظيفه fibonacci سوف تستدعى نفسها اكثر من 400 مره وسينتهى الامر إلى تجميد واجهه المستخدم حتى ينتهى من حساب الرقم ، او رساله تخبرك بأن تقوم بإلغاء تنفيذ هذا التطبيق إن قمت بحساب رقم فيبوناتشى لرقم كبير مثل 100 :D .

لكن الامر الان اصبح مختلف بعد تقديم Worker api فى Google geras الذى تم معايرته من قبل WHATWG و تم ادراجه فى متصفح FireFox 3.5 و Safari 4 و Chrome 2 و تطبيقات سطح المكتب التى يتم برمجتها ب AIR 2.0 او titanium و تطبيقات الموبايل التى يتم برمجتها ب titanium mobile و ايضا يمكنك استخدامه فى برمجه امتدادات الفايرفوكس و بإنتظار IE و Opear ، الان تطبيقات الجافاسكربت يمكننها انشاء threads لتقليل الحمل من على ال thread الرئيسى ، حيث يمكنك الان تشغيل ملفات جافاسكربت فى thread خلفى -background thread- لن يؤثر على ال thread الرئيسى ، و يمكن ايضا لل background thread انشاء threads اخرى لتعاونه فى انجاز عمله ، حيث يتم الاتصال بين ال threads المختلفه عن طريق الحدث onmessage و الوظيفه postMessage كما سنرى لاحقا ، يمكن لل threads المختلفه تبادل الرسائل فى صوره نصوص او اعداد او قيم منطقيه او JSON لكن لابد ان لا يحتوى على function او cyclical reference .

انشاء worker جديد

يمكنك انشاء thread خلفى بمنتهى البساطه عن طريق تمرير مسار ملف الجافاسكربت الذى سيعمل فى thread خلفى Worker constructor كما يوضح الكود البسيط التالى :

var myWorker = new Worker("fibbonaci.js");

عن طريق السطر السابق سيعمل ملف fibonacci.js داخل thread منفصل لحساب رقم فيبوناتشى بدون تجميد واجهه المستخدم او التأثير على كفائه ال thread الرئيسى ، و من خلال تسجيل مستمع لحدث onmessage ل myWorker يمكننا تلقى رسائل من ال thread الخاص ب fibonacci.js كما يوضح الكود البسيط التالى :

var myWorker = new Worker("fibbonaci.js");
myWorker.onmessage = function(event){
    alert(event.data);
};

داخل داله مستمع الحدث onmessage الخاصيه data للكائن event تشير إلى البيانات التى يرسلها ال thread الخلفى ، يمكننا ارسال رساله إلى ال thread الخاص ب fibonacci.js عن طريق الوظيفه postMessage كما يوضح الكود البسيط التالى :

var myWorker = new Worker("fibbonaci.js");
myWorker.onmessage = function(event){
    alert(event.data);
};
myWorker.postMessage(11);

السطر الاول فى الكود السابق يقوم بإنشاء thread جديد للملف fibonacci.js ، و السطر الثانى يسجل مستمع لحدث message لتلقى اى رساله يرسلها ال thread الخاص ب fibonacci.js ، اما السطر الرابع و الاخير يقوم بإرسال القيمه 11 إلى ال thread الخاص ب fibonacci.js حتى يقوم بالعمل عليها ، اى حساب رقم فيبوناتشى للرقم 11 فى ال thread منفصل .

الكود داخل fibonacci.js يقوم بالإستماع للحدث message و عندما يتلقى رساله برقم ما ، يقوم بحساب رقم الفيبوناتشى له و ارساله عن طريق الوظيفه postMessage إلى ال thread الرئيسى كما يوضح الكود البسيط التالى :

/* inside fibonacci.js file */
onmessage = function(event){
    var num = parseInt(event.data); // get the message as integer
    var fibNum = fibonacci(num); // get the fibonacci number
    postMessage(fibNum); // post the result to the main thread
}
function fibonacci(n){
    if(n==0 || n==1){
        return n;
    } else {
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
}
/* end of fibonacci.js file */

عندما يستقبل رساله يستخرج منها الرقم من خلال event.data ثم يحصل على رقم فيبوناتشى عن تمرير الرقم إلى وظيفه fibonacci و فى النهايه يقوم بإرسال النتيجه إلى ال thread الرئيسى عن طريق postMessage .

القاعده العامه هى ان ال thread يقوم بإرساله رسائل إلى thread اخر عن طريق الوظيفه postMessage و يقوم بإستقبال رسائل ال threads الاخرى من خلال مستمع الحدث onmessage .

إنهاء عمل worker قديم

اذا كنت تريد ايقاف عمل worker تم انشاءه من قبل ، قم بإستدعاء الوظيفه terminate الخاصه بهذا ال worker ، حيث سيتم قتل ال thread الخاص به بدون اكمال عمله مباشره كما يوضح الكود البسيط التالى:

myWorker.terminate(); // close and kill myWorker thread

بعض القيود المفروضه على worker

الكود الذى يعمل فى thread جديد عن طريق worker لايمكنه الوصول إلى ال DOM ، اذا كان هناك تطبيق يقوم بتعديل ال DOM فإن هذه العمليه لابد ان يتولاها ال thread الرئيسى ، ال thread الفرعى عليه العمليات المعقده فقط ، يمكن لل thread الفرعى استخدام ال XMLHttpRequest لإجراء اتصالات اجاكس لكنه لا يستطيع الوصول إلى XMLResponse او channel ، لا يمكن لل thread الفرعى ارسال function او cyclic reference إلى ال thread الرئيسى ، يتم تحويل البيانات تلقائيا إلى صيغه JSON بعد تنقيحها ، و يمكنك ايضا استخدام setTimeout و clearTimeout و setInterval و clearInterval .

الكائن navigator

يمكنك الوصول إلى الكائن navigator من داخل ال thread الفرعى للحصول على معلومات عن المتصفح ، يحتوى الكائن navigator على اربع خواص :

navigator.appName
navigator.appVersion
navigator.platform
navigator.userAgent

إستيراد الملفات من داخل ال worker

يمكنك اسيراد ملفات الجافاسكربت من داخل ال worker عن طريق استخدام وظيفه imortScripts التى تقبل مسار الملفات التى تريد استيرادها ، يتم تحميل الملفات بأى ترتيب لكن يتم تنفيذها بالترتيب الذى تريد عند تمريرها للوظيفه كما يوضح الكود البسيط التالى :

importScripts(); // imports nothing
importScripts("foo.js"); // imports one file
importScripts("foo.js", "bar.js"); // imports two files

التعامل مع أخطاء ال worker

عندما يحدث خطأ فى ال worker سيتم استدعاء مستمع الحدث الخاص ب onerror المرتبط به و سيتم تمرير لمستمع الحدث الكائن event الذى يحتوى على 3 خصائص تحتوى على معلومات عن الخطأ و هى message و filename و lineno ، يمكن منع الخطأ من خلال الوظيفه preventDefault كما يوضح الكود التالى :

var myWorker = new Worker("worker.js");
myWorker.onerror = function(event){
    event.message; // human readable error message
    event.lineno; // error line number
    event.filename; // file name at which error occurs
    event.preventDefault(); // prevent error propagation
}

للمزيد

هذا ليس كل شىء هناك الكثير من الكائنات لم يغطيها هذا المقال ، مواصفات worker api لن تكتمل و يمكن تغيرها او زياده ميزات اخرى فى المتصفحات ، هناك ايضا بعض الكائنات لم يتم ادراجها مثل SharedWorker و DedicatedWorker و غيره ، يمكنك قراءه مواصفات ال web workers كامله من هنا ، بالإضافه إلى مقاله usinng web workers من موزيلا ، و هذه التدوينه من مدونه mozilla developers center .


تابع

Get every new post delivered to your Inbox.