javascript中的promise和deferred:实践(二)

介绍:

在第一节呢,我花了大量的时间来介绍promises和deferreds的理论。现在呢,我们来看看jquery中的promises(作者一会儿用单数,一会儿用复数形式,妹的)。

Note:代码示例将使用jQuery,尽管它偏离了Promise/A 协议。

排序模式:

deferred就是一个未完成的对象,promise呢则是一个未知的值。换句话说,prmises/deferreds 允许我们描述(represent)简单的任务,可以很容易地组合来描述复杂的任务和任务流,允许我们细粒度地控制排序。这就意味着我们可以像写同步代码一样去写异步代码,so easy,妈妈再也不用担心我的学习了。此外,promises让复杂的异步任务变得更容易去抽象成一些小块的功能--比如动画加载,动画处理等等。

让我们来看看三种常见的排序模式,promises使之变成了可能:堆放,并行和顺序。

  • 堆放:同一promise 事件绑定多个处理

    var request = $.ajax(url);
    
      request.done(function(){
    console.log('Request completed');});// Somewhere else in the application request.done(function(retrievedData){
    $('#contentPlaceholder').html(retrievedData);});
  • 并行:请求多个promise,返回一个promise,该promise可以给出执行多个promise的完成情况。
    $.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){vardef=new $.Deferred();
    
            setTimeout(function(){
    console.log('Request completed');def.resolve();},2000);returndef.promise();},function(err){
    console.log('Step1 failed: Ajax request');});
    step2.done(function(){
    console.log('Sequence completed')
    setTimeout("console.log('end')",1000);});

这些模式可以组合使用或者单独使用,用以建立复杂的任务或工作流。

常见示例:

许多的promise示例都使用Ajax请求和UI动画。实际上,jQuery的Ajax请求默认返回的是个promise。这给人造成一种错觉,以为解决异步任务完美解决方案就是promise了。其实不然,promise就是一个值得你在任何时候去考虑使用的工具,而不仅仅是回调。让我们看看使用promise的实例吧。

  • ajax,这个就算了,一搜一大把,我就略过啦。
  • 定时:创建一个基于timeout的函数:
    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!});
  • 动画:显然下面的动画是没啥实际用处的,但是这个示例却给出了promise和动画如何一起使用。
    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 objectreturn 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});
  • 使用$.when()同步并行任务
    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');});
  • 解耦事件和程序逻辑(jsfiddle demo
    vardef, getData, updateUI, resolvePromise;// The Promise and handlerdef=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){thrownewError("Error getting the data");});// Event Handler
    resolvePromise =function(ev){
    ev.preventDefault();def.resolve(ev.type,this);returndef.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: 理解jQuery中的.then()

为了演示一对“gotcha's”,这些最终的实例将会贯穿我的整个promise实践。

让我们来创建两个公用函数:

// Utility Functionsfunction wait(ms){var deferred = $.Deferred();
setTimeout(deferred.resolve, ms);return deferred.promise();}function notifyOfProgress(message, promise){
console.log(message + promise.state());}

第一次的promise链式写法看起来就像这样:

// Naive attempt at working with .then()// Create two new deferred objectsvar aManualDeferred =new $.Deferred(),
secondManualDeferred = aManualDeferred.then(function(){
console.log('1 started'); wait(3500).done(function(){
console.log('1 ended');});});// After secondManualDeferred is resolvedsecondManualDeferred.then(function(){
console.log('2 started'); wait(2500).done(function(){
console.log('2 ended');});});// Resolve the first promise
aManualDeferred.resolve();

执行的输出结果:

1 started
2 started
2 ended
1 ended

纳尼?jQuery API不是说 .then()方法可以链式并返回promise么?我所期望的是无论我在.then()方法中插入了任何代码,程序都应顺序执行,只有上一个任务完成了,才可以执行下一个。但是这很明显不是我所期望的结果啊?为毛啊?

.then()原理

查看jQuery 源代码,我们可以发现:

  • .then()方法始终返回一个新的promise
  • .then()必须传递一个函数作为回调

如果.then()没有传递函数,那么:

  • 新的promise将会拥有和初始promise行为一致(这就意味着它立即 被解决/被拒绝)
  • .then()中的输入将被执行但是会被.then()忽略

如果.then()被传递了一个函数,该函数返回了一个promise 对象,那么:

  • 新的promise将和返回的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();
  • 如果.then()传递的是一个函数,该函数返回一个值,那么这个值将成为新对象的值
    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拥有同样的值。

避免被回调坑爹(Avoiding the descent into callback hell)

我们知道.then()需要一个回调函数并返回一个promise,所以我们可以像下面这么做:

// Anti-pattern - Return to callback hellvar 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了,我们又掉到回调嵌套的坑里了。还好,我们有绝招来规避这种嵌套。那么,如何解决这个问题呢,当然,这得具体情况具体分析咯。

避免使用大量的无名promise

举例如下:

// A chain// Create new deferred objectsvar 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和处理函数

假如我们不想深层嵌套,我们就得命名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 statethirdManualDeferred.notify(
notifyOfProgress('thirdManualDeferred ', thirdManualDeferred));// Resolve the first promiseaManualDeferred.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(){vardef=new $.Deferred();

    setTimeout(function(){def.resolve('50');},2000);returndef.promise();};

passingData().done(function(value){
console.log(value);});

当我们 resolve 了一个promise,我们可以给它设置 this。

// Create an objectvar myObject ={
myMethod:function(myString){
console.log('myString was passed from', myString);}};// Create deferredvar 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 中的一些方法介绍,作者一带而过,我就不翻译了。想看,就看原文吧。

译自:http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use

 
 
 
标签: jqueryjavascript

javascript中的promise和deferred:实践(二)的更多相关文章

  1. 快速入门上手JavaScript中的Promise

    当我还是一个小白的时候,我翻了很多关于Promise介绍的文档,我一直没能理解所谓解决异步操作的痛点是什么意思 直到我翻了谷歌第一页的所有中文文档我才有所顿悟,其实从他的英文字面意思理解最为简单粗暴 ...

  2. JavaScript中的Promise【期约】[未完成]

    JavaScript中的Promise[期约] 期约主要有两大用途 首先是抽象地表示一个异步操作.期约的状态代表期约是否完成. 比如,假设期约要向服务器发送一个 HTTP 请求.请求返回 200~29 ...

  3. 通过一道笔试题浅谈javascript中的promise对象

    因为前几天做了一个promise对象捕获错误的面试题目,所以这几天又重温了一下promise对象.现在借这道题来分享下一些很基础的知识点. 下面是一个面试题目,三个promise对象捕获错误的例子,返 ...

  4. Javascript中的Promise

    Promise定义 Promise是CommonJs的规范之一,包含resolve,reject,done,fail,then等方法,能够帮助我们控制代码的流程,避免函数的多层嵌套.异步在web开发中 ...

  5. 掌握JavaScript中的Promise,实现异步编程

    事件循环 基本介绍 JavaScript是一门单线程的编程语言,所以没有真正意义上的并行特性. 为了协调事件处理.页面交互.脚本调用.UI渲染.网络请求等行为对主线程造成的影响,事件循环(event ...

  6. JavaScript中你所不知道的Object(二)--Function篇

    上一篇(JavaScript中你所不知道的Object(一))说到,Object对象有大量的内部属性,而其中多数和外部属性的操作有关.最后留了个悬念,就是Boolean.Date.Number.Str ...

  7. JavaScript中实现DI的原理(二)

    JavaScript中实现DI的原理 在JavaScript中实现DI,看起来难,实际上原理很简单,它的核心技术是Function对象的toString().我们都知道,对一个函数对象执行toStri ...

  8. 彻底理解Javascript 中的 Promise(-------------------------------***---------------------------------)

    ES6原生提供了 Promise 对象. 到底是何方妖怪呢?打出来看看: 所谓 Promise,就是一个对象,用来传递异步操作的消息.它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个 ...

  9. javascript中的Promise使用

    参考自: http://m.jb51.net/article/102642.htm 1.基本用法: (1).首先我们new一个Promise,将Promise实例化 (2).然后在实例化的promis ...

随机推荐

  1. Android游戏开发研究与主角在地图滚动

     让人感动的地图过程平滑滚动         玩过rpg朋友应该都知道RPG的游戏地图一般都比較大 今天我和大家分享一下在RPG游戏中怎样来处理超出手机屏幕大小的游戏地图. 如图所看到的为程序效果 ...

  2. 国籍控件(js源码)

    国籍控件(js源码) 一直苦于没有好的国籍控件可以用,于是抽空写了一个国籍控件,现分享给大家. 主要功能和界面介绍 国籍控件主要支持中文.英文过滤以及键盘上下事件. 源码介绍 国籍控件核心是两个文件, ...

  3. inux平台的C与C++

    课堂里学不到的C与C++那些事(一) 首先,声明一下这是一个系列的文章.至于整个系列有多少篇,笔者也不知道,不知道有多少篇,也不知道多久会更新一篇.反正只有一个原则,写出来的文 章能见得人才会公布出来 ...

  4. java中Integer包装类的具体解说(java二进制操作,全部进制转换)

    程序猿都非常懒,你懂的! 今天为大家分享的是Integer这个包装类.在现实开发中,我们往往须要操作Integer,或者各种进制的转换等等.我今天就为大家具体解说一下Integer的使用吧.看代码: ...

  5. 19.最经济app发短信的方法

    在创业团队.一个重要的原则是能省就省,该花的花,明智地使用金钱. 今的app,为了获取用户的社交关系.须要用户的手机号注冊. 用手机号注冊就涉及到一个发送短信验证码的问题,那怎么才干在短信服务上投入最 ...

  6. Spring之SpringMVC(源码)启动初始化过程分析

    1.说明 SpringMVC作为Spring提供的MVC实现,可以实现与Spring的天然无缝联合,因为具有很广泛的用途.具体的关于SpringMVC的处理流程逻辑我在这里就不在赘述了.还是来通过源码 ...

  7. 大批量烧写openwrt系统

    http://wiki.openwrt.org/toh/tp-link/tl-wr1043nd OEM mass flashing Flashing hundreds of devices using ...

  8. ubuntu下Eclipse无法启动

    我的Ubuntu是12.04 LTS版的.jdk是官网下载后解压就可以用的.配置好PATH和CLASSPATH后,双击Eclipse弹出这个窗口: 但是如果通过终端,以命令行的方式执行 ./eclip ...

  9. 使用JSmooth制造java jar文件可以运行exe文件教程图像

    这是我之前在个人博客3yj上面写的一篇文章,如今转载过来,原文地址 (这不是广告哦) 几年前,刚接触java的是.就想用一些方法把自己的劳动果实保护起来,曾经也用过非常多这种工具.有一个特别好用,今天 ...

  10. .NET 相依性注入

    发布<.NET 依賴注入>电子书 beta 版 书籍进度 本书目前已经开始发行 beta 版,完成进度约 70%.(我希望这本书不要超过 200 页,目前看起来应该没问题.) 简介 本书内 ...