سؤال

انا اقرأ هذه المقالة ويبدو أن القسم الخاص بالتجريد الوعد معقد للغاية بالنسبة لي. يتم إعطاء ما يلي كمثال:

requestSomeData("http://example.com/foo") // returns a promise for the response
    .then(function(response){ // ‘then’ is used to provide a promise handler
        return JSON.parse(response.body); // parse the body
    }) // returns a promise for the parsed body
    .then(function(data){
        return data.price; // get the price
    }) // returns a promise for the price
    .then(function(price){ // print out the price when it is fulfilled
        print("The price is " + price);
    });

يبدو لي أن ما يلي يمكن أن يوفر نفس النتيجة مع عدد أقل من خطوط التعليمات البرمجية:

requestSomeData("http://example.com/foo")
    .requestHandler(function(response){
        // parse the body
        var data  = JSON.parse(response.body);

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });
هل كانت مفيدة؟

المحلول

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

هذا ما يعدك به: القدرة القوية على تأجيل حساب الإجابة الصحيحة حتى وقت أكثر ملاءمة. كما يوحي الاسم ، فإن البناء "يعد" بإعطائك النتيجة في مرحلة ما ، وليس بالضرورة الآن. يمكنك قراءة المزيد عن العقود المستقبلية والوعود العمل على نطاق أوسع هنا.

نصائح أخرى

دعنا نقارن مثال الوعد بمثال جافا سكريبت نقي:

// First we need a convenience function for W3C's fiddly XMLHttpRequest.
// It works a little differently from the promise framework.  Instead of 
// returning a promise to which we can attach a handler later with .then(),
// the function accepts the handler function as an argument named 'callback'.

function requestSomeDataAndCall(url, callback) {
    var req = new XMLHttpRequest();
    req.onreadystatechange = resHandler;
    req.open("GET", url, false);
    req.send();
    function resHandler() {
        if (this.readyState==4 && this.status==200) {
            callback(this);
        } else {
            // todo: Handle error.
        }
    }
}

requestSomeDataAndCall("http://example.com/foo", function(res){
    setTimeout(function(){
        var data = JSON.parse(res.responseText);
        setTimeout(function(){
            var price = data.price;
            setTimeout(function(){
                print("The price is "+price);
            },10);
        },10);
    },10);
});

كما أشار نوربرت هارتل ، ستعلق json.parse () المتصفح للسلاسل الكبيرة. لذلك استخدمت setTimeOut () لتأخير تنفيذها (بعد توقف قدره 10 مللي ثانية). هذا مثال على حل كريس كوال. يسمح لخيط JavaScript الحالي بإكمال ، وتحرير المتصفح لتقديم تغييرات DOM وتمرير الصفحة للمستخدم ، قبل تشغيل رد الاتصال.

آمل أن يستخدم إطار عمل CommonJS أيضًا شيئًا مثل SetTimeout ، وإلا فإن الوعود اللاحقة في مثال المقالة ستعمل بالفعل بشكل متزامن كما كان يخشى.

البديل الخاص بي أعلاه يبدو قبيحًا للغاية ، حيث تتطلب العمليات اللاحقة المزيد من المسافة البادئة. لقد قمت بإعادة هيكلة الرمز ، حتى نتمكن من توفير سلسلة العمليات الخاصة بنا في مستوى واحد:

function makeResolver(chain) {
    function climbChain(input) {
        var fn = chain.shift();      // This particular implementation
        setTimeout(function(){       // alters the chain array.
            var output = fn(input);
            if (chain.length>0) {
                climbChain(output);
            }
        },10);
    }
    return climbChain;
}

var processChain = [
    function(response){
        return JSON.parse(response.body);
    },
    function(data){
        return data.price; // get the price
    },
    function(price){
      print("The price is " + price);
    }
];

var climber = makeResolver(promiseChain);
requestSomeDataAndCall("http://example.com/foo", climber);

كنت آمل أن أثبت أن التماس التقليدي لعمليات الاسترجاعات في JavaScript يعادل إلى حد كبير الوعود. ولكن بعد محاولتين ، يبدو أنني قد أظهرت ، مع الإشارة إلى نظام الرمز في المثال الأصلي ، أن الوعود هو حل أكثر أناقة!

المقتطف الثاني عرضة لإنكار هجوم الخدمة لأن example.com/foo يمكنه فقط إرجاع JSON غير صالح لتحطم الخادم. حتى الاستجابة الفارغة غير صالحة JSON (على الرغم من صحة JS). انها مثل mysql_* أمثلة مع فتحات حقن SQL الصارخة.

ويمكن تحسين رمز الوعد كثيرًا أيضًا. هذه متساوية:

requestSomeData("http://example.com/foo") // returns a promise for the response
    .then(function(response){ // ‘then’ is used to provide a promise handler
        // parse the body
        var data  = JSON.parse(response.body);

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });

و:

requestSomeData("http://example.com/foo")
    .requestHandler(function(response){
        try {
            var data = JSON.parse(response.body);
        }
        catch(e) {
            return;
        }

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });

إذا أردنا التعامل مع الخطأ ، فستكون هذه متساوية:

requestSomeData("http://example.com/foo") // returns a promise for the response
    .then(function(response){ // ‘then’ is used to provide a promise handler
        // parse the body
        var data  = JSON.parse(response.body);

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    }).catch(SyntaxError, function(e) {
        console.error(e);
    });

و:

requestSomeData("http://example.com/foo")
    .requestHandler(function(response){
        try {
            var data = JSON.parse(response.body);
        }
        catch(e) {
            //If the above had a typo like `respons.body`
            //then without this check the ReferenceError would be swallowed
            //so this check is kept to have as close equality as possible with
            //the promise code
            if(e instanceof SyntaxError) {
                console.error(e);
                return;
            }
            else {
                throw e;
            }
        }

        // get the price
        var price = data.price;

        // print out the price
        print("The price is " + price);
    });

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top