在第一节呢,我花了大量的时间来介绍promises和deferreds的理论。如今呢,咱们来看看jquery中的promises(做者一下子用单数,一下子用复数形式,妹的)。javascript
Note:代码示例将使用jQuery,尽管它偏离了Promise/A 协议。css
deferred就是一个未完成的对象,promise呢则是一个未知的值。换句话说,prmises/deferreds 容许咱们描述(represent)简单的任务,能够很容易地组合来描述复杂的任务和任务流,容许咱们细粒度地控制排序。这就意味着咱们能够像写同步代码同样去写异步代码,so easy,妈妈不再用担忧个人学习了。此外,promises让复杂的异步任务变得更容易去抽象成一些小块的功能--好比动画加载,动画处理等等。html
让咱们来看看三种常见的排序模式,promises使之变成了可能:堆放,并行和顺序。java
var request = $.ajax(url); request.done(function () { console.log('Request completed'); }); // Somewhere else in the application request.done(function (retrievedData) { $('#contentPlaceholder').html(retrievedData); });
$.when(taskOne, taskTwo).done(function () { console.log('taskOne and taskTwo are finished'); });
var step1, step2, url; url = 'http://fiddle.jshell.net'; step1 = $.ajax(url); step2 = step1.then( function (data) { var def = new $.Deferred(); setTimeout(function () { console.log('Request completed'); def.resolve(); },2000); return def.promise(); }, function (err) { console.log('Step1 failed: Ajax request'); } ); step2.done(function () { console.log('Sequence completed') setTimeout("console.log('end')",1000); });
这些模式能够组合使用或者单独使用,用以创建复杂的任务或工做流。jquery
许多的promise示例都使用Ajax请求和UI动画。实际上,jQuery的Ajax请求默认返回的是个promise。这给人形成一种错觉,觉得解决异步任务完美解决方案就是promise了。其实否则,promise就是一个值得你在任什么时候候去考虑使用的工具,而不只仅是回调。让咱们看看使用promise的实例吧。web
function wait(ms) { var deferred = $.Deferred(); setTimeout(deferred.resolve, ms); // We just need to return the promise not the whole deferred. return deferred.promise(); } // Use it wait(1500).then(function () { // Do something brilliant here! });
var fadeIn = function (el) { var promise = $(el).animate({ opacity: 1 }, 1500); // Dynamically create and return an observable promise object which will be resolved when the animation completes. return promise.promise(); }; var fadeOut = function(el) { var promise = $(el).animate({ opacity: 0 }, 1500); // Dynamically create and return an observable promise object return promise.promise(); }; // With the setup out of the way, we can now do one of the following. // Parallel $.when( fadeOut('div'), fadeIn('div') ).done(function () { console.log('Animation finished'); $('p').css('color', 'red'); }); // OR // Chained fadeOut('div').then(function (el) { fadeIn(el); // returns a promise }).then(function (el) { fadeOut(el); // returns a promise });
var promiseOne, promiseTwo, handleSuccess, handleFailure; // Promises promiseOne = $.ajax({ url: '../test.html' }); promiseTwo = $.ajax({ url: '../test.html' }); // Success callbacks // .done() will only run if the promise is successfully resolved promiseOne.done(function () { console.log('PromiseOne Done'); }); promiseTwo.done(function () { console.log('PromiseTwo Done'); }); // $.when() creates a new promise which will be: // resolved if both promises inside are resolved // rejected if one of the promises fails $.when( promiseOne, promiseTwo ) .done(function () { console.log('promiseOne and promiseTwo are done'); }) .fail(function () { console.log('One of our promises failed'); });
var def, getData, updateUI, resolvePromise; // The Promise and handler def = new $.Deferred(); updateUI = function (data) { $('p').html('I got the data!'); $('div').html(data); }; getData = $.ajax({ url: '/echo/html/', data: { html: 'testhtml', delay: 3 }, type: 'post' }) .done(function(resp) { return resp; }) .fail(function (error) { throw new Error("Error getting the data"); }); // Event Handler resolvePromise = function (ev) { ev.preventDefault(); def.resolve(ev.type, this); return def.promise(); }; // Bind the Event $(document).on('click', 'button', resolvePromise); def.then(function() { return getData; }) .then(function(data) { updateUI(data); }) .done(function(promiseValue, el) { console.log('The promise was resolved by: ', promiseValue, ' on ', el); }); // Console output: The promise was resolved by: click on <button> </button>
为了演示一对“gotcha's”,这些最终的实例将会贯穿个人整个promise实践。ajax
让咱们来建立两个公用函数:shell
// Utility Functions function wait(ms) { var deferred = $.Deferred(); setTimeout(deferred.resolve, ms); return deferred.promise(); } function notifyOfProgress(message, promise) { console.log(message + promise.state()); }
第一次的promise链式写法看起来就像这样:promise
// Naive attempt at working with .then() // Create two new deferred objects var aManualDeferred = new $.Deferred(), secondManualDeferred = aManualDeferred.then(function () { console.log('1 started'); wait(3500).done(function () { console.log('1 ended'); }); }); // After secondManualDeferred is resolved secondManualDeferred.then(function () { console.log('2 started'); wait(2500).done(function () { console.log('2 ended'); }); }); // Resolve the first promise aManualDeferred.resolve();
执行的输出结果:app
1 started 2 started 2 ended 1 ended
纳尼?jQuery API不是说 .then()方法能够链式并返回promise么?我所指望的是不管我在.then()方法中插入了任何代码,程序都应顺序执行,只有上一个任务完成了,才能够执行下一个。可是这很明显不是我所指望的结果啊?为毛啊?
查看jQuery 源代码,咱们能够发现:
若是.then()没有传递函数,那么:
若是.then()被传递了一个函数,该函数返回了一个promise 对象,那么:
var deferred = $.Deferred(), secondDeferred = deferred.then(function () { return $.Deferred(function (newDeferred) { setTimeout(function() { console.log('timeout complete'); newDeferred.resolve(); }, 3000); }); }), thirdDeferred = secondDeferred.then(function () { console.log('thirdDeferred'); }); secondDeferred.done(function () { console.log('secondDeferred.done'); }); deferred.resolve();
var deferred = $.Deferred(), filteredValue = deferred.then(function (value) { return value * value; }); filteredValue.done(function (value) { console.log(value); }); deferred.resolve(2); // 4
你可能已经注意到了,为毛个人版本没法运行(但是我能运行啊,做者,你肿么了)。我没有当即从.then()返回一个promise,因此由.then()建立的新的promise拥有一样的值。
咱们知道.then()须要一个回调函数并返回一个promise,因此咱们能够像下面这么作:
// Anti-pattern - Return to callback hell var aManualDeferred = new $.Deferred(); aManualDeferred.then(function () { console.log('1 started'); return wait(3500).then(function () { console.log('1 ended'); }).then(function () { console.log('2 started'); return wait(2500).done(function () { console.log('2 ended'); }); }); }); // Resolve the first promise aManualDeferred.resolve();
运行啦。不幸的是,这个回调太shit了,咱们又掉到回调嵌套的坑里了。还好,咱们有绝招来规避这种嵌套。那么,如何解决这个问题呢,固然,这得具体状况具体分析咯。
举例以下:
// A chain // Create new deferred objects var aManualDeferred = $.Deferred(); aManualDeferred.then(function () { console.log('1 started'); // We need to return this, we return a new promise which is resolved upon completion. return wait(3500); }) .then(function () { console.log('1 ended'); }) .then(function () { console.log('2 started'); return wait(2500); }) .then(function () { console.log('2 ended'); }); // Resolve the first promise aManualDeferred.resolve();
此次看起来就漂亮多啦。缺点是,只有一个promise是命名的,不利于咱们细粒度地控制每一个步骤,这个在不少状况下是很是有用的哦。
假如咱们不想深层嵌套,咱们就得命名promise,这样咱们就有了每一个步骤的控制权。
看看最终版本:
var aManualDeferred, secondManualDeferred, thirdManualDeferred; // Create two new deferred objects aManualDeferred = $.Deferred(); secondManualDeferred = aManualDeferred.then(function () { console.log('1 started'); // We need to return this, we return a new promise which is resolved upon completion. return wait(3500); }) .done(function () { console.log('1 ended'); }); thirdManualDeferred = secondManualDeferred.then(function () { console.log('2 started'); return wait(2500); }) .done(function () { console.log('2 ended'); }); // Check current state thirdManualDeferred.notify( notifyOfProgress('thirdManualDeferred ', thirdManualDeferred) ); // Resolve the first promise aManualDeferred.resolve(); // Console output // aManualDeferred pending // secondManualDeferred pending // 1 started // 1 ended // 2 started // 2 ended
这个优势就很明显了,如今的程序分为三个步骤,咱们能够访问每一个promise的状态,用以发送进程通知,或者在管理代码执行顺序时,也不须要重写代码(谁说的,只不过修改代价很小了)。
在Ajax示例中,咱们看到,能够给.resolve()和.fail()函数传值。若是一个promise resolved(我以为“resolved”这里仍是不翻译的好)了一个值,那么新的promise就是返回值自己。
var passingData = function () { var def = new $.Deferred(); setTimeout(function () { def.resolve('50'); }, 2000); return def.promise(); }; passingData().done(function (value) { console.log(value); });
当咱们 resolve 了一个promise,咱们能够给它设置 this。
// Create an object var myObject = { myMethod: function (myString) { console.log('myString was passed from', myString); } }; // Create deferred var deferred = $.Deferred(); // deferred.done(doneCallbacks [, doneCallbacks ]) deferred.done(function (method, string) { console.log(this); // myObject // myObject.myMethod(myString); this[method](string); }); deferred.resolve.call(myObject, 'myMethod', 'the context'); => myString was passed from the context // We could also do this: // deferred.resolveWith(myObject, ['myMethod', 'resolveWith']); // but it's somewhat annoying to pass an array of arguments. // => myString was passed from resolveWith
剩下的是最佳实践和jquery 中的一些方法介绍,做者一带而过,我就不翻译了。想看,就看原文吧。
ps:
全部的deffered对象都支持的方法:
------------------------
then(doneCallbacks,failCallbacks):由此可知,then能够处理成功和失败的回调,这就是和done的区别啊
done(doneCallbacks)
fail(failCallbacks)
---------------
ajax对象还包括
sucess
fail
它们会分别映射到deffered对象的done 和fail方法上
译自:http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use