2010-01-29 8 views
13

Я читаю this article, и раздел об абстракции обещания кажется мне слишком сложным. Ниже приводится в качестве примера:Какая польза от «обещания» абстракции в CommonJS?

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); 
    }); 
+2

Вы правы, нет смысла использовать обещания для синхронных операций. Таким образом, результат должен быть равен. Но тогда это пример и иллюстрирует использование обещания. Для кода, который работает после вашего примера, действительно есть разница. Если вам нужно запустить что-то после примера, чем вы можете это сделать (используя обещающий подход), не зная ничего о том, что делает код примера –

ответ

17

Хотя это правда, что и в конечном итоге достичь То же самое, разница в том, что ваш второй пример не является асинхронным. Например, подумайте, что произойдет, если JSON.parse(...) окажется чрезвычайно дорогостоящей операцией; вам придется висеть, пока все не закончится, что может не всегда быть тем, что вы хотите.

Это то, что обещает вам: мощная способность отложить вычисление правильного ответа до более удобного времени. Как следует из названия, конструкция «обещает» дать вам результат в какой-то момент, просто не обязательно прямо сейчас. Вы можете больше узнать о фьючерсах и обещаниях работать в более крупном масштабе here.

+2

. Какое удобное время здесь? Если операция чрезвычайно дорога, а JSON.parse - это часть кода javascript, она все равно будет висеть. Разница заключается только в том, что с обещанием вы можете закончить фактически выполняемые функции. –

+3

Верно, что синтаксический анализ будет занимать одинаковое время независимо от того, будет ли он вычисляться синхронно или асинхронно. Однако, если вы потратите время на реализацию своего синтаксического анализатора таким образом, чтобы он предсказуемо возвращался к циклу событий после завершения небольших операций, другой асинхронный код может выполняться асинхронно между каждым блоком. Это делает приложение более отзывчивым, а не быстрее. –

+0

Или JSON.parse может быть родным методом и выполнять в другом потоке – Jan

3

Давайте сравним пример обещания чистого например Javascript:

// 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); 
}); 

Как Norbert Hartl отметил, 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 довольно много эквивалентны обещания. Однако после двух попыток, которые, как я показал, со ссылкой на аккуратность кода в исходном примере, обещает гораздо более элегантное решение!

0

Можно также добавить, что преимущество первой версии над вторым состоит в том, что она разделяет различные операции в цепочке уточнения (функции не обязательно должны быть записаны на месте). Вторая версия сочетает в себе как низкоуровневый синтаксический анализ с логикой приложения. В частности, использование принципов SOLID в качестве рекомендаций, вторая версия нарушает как OCP, так и SRP.

1

Второй фрагмент уязвим для атаки на отказ в обслуживании, потому что 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); 
    }); 

 Смежные вопросы

  • Нет связанных вопросов^_^