ES6中的Promise

什么叫promise?

Promise对象可以理解为一次执行的异步操作,使用promise对象之后可以使用一种链式调用的方式来组织代码;让代码更加的直观。

那我们为什么要使用promise?

比如我们在工作中经常会碰到这么一个需求,比如我使用ajax发一个A请求后,成功后拿到数据,我们需要把数据传给B请求;那么我们需要如下编写代码:

$.ajax({url:'',dataType:'json',success:function(data){// 获取data数据 传给下一个请求varid = data.id;          $.ajax({url:'',data:{"id":id},success:function(){// .....}          });      }});

如上代码;上面的代码有如下几点缺点:

1.后一个请求需要依赖于前一个请求成功后,将数据往下传递,会导致多个ajax请求嵌套的情况,代码不够直观。

2.如果前后两个请求不需要传递参数的情况下,那么后一个请求也需要前一个请求成功后再执行下一步操作,这种情况下,那么也需要如上编写代码,导致代码不够直观。

如何创建promise对象?

要想创建promise对象,可以使用new来调用promise的构造器来进行实例化。

如下代码:

varpromise =newPromise(function(resolve,reject){// 异步处理// 成功调用resolve 往下传递参数 且只接受一个参数// 失败调用reject  往下传递参数 且只接受一个参数});

对通过new 生成的promise对象为了设置其值在resolve(成功) / reject(失败) 时调用的回调函数,可以使用promise.then()实例方法。

如下代码:

promise.then(onFulfilled, onRejected);

resolve(成功) 时 调用onFulfilled 方法,reject(失败) 时 调用onRejected方法;

Promise.then 成功和失败时都可以使用,如果出现异常的情况下可以采用

promise.then(undefined,onRejected) 这种方式,只指定onRejected回调函数即可,不过针对这种情况下我们有更好的选择是使用catch这个方法;代码如下:

promise.catch(onRejected);

上面啰嗦了这么多,我们来分别来学习相关的promise对象中的方法知识点吧!

理解Promise.resolve

一般情况下我们都会使用new Promise()来创建promise对象,但是我们也可以使用promise.resolve 和 promise.reject这两个方法;

Promise.resolve(value)的返回值也是一个promise对象,我们可以对返回值进行.then调用;如下代码:

Promise.resolve(11).then(function(value){console.log(value);// 打印出11});

resolve(11)代码中,会让promise对象进入确定(resolve状态),并将参数11传递给后面的then所指定的onFulfilled 函数;

我们上面说过创建promise对象,可以使用new Promise的形式创建对象,但是我们这边也可以使用Promise.resolve(value)的形式创建promise对象;

理解Promise.reject

Promise.reject 也是new Promise的快捷形式,也创建一个promise对象,比如如下代码:

Promise.reject(newError(“我错了,请原谅俺!!”));

就是下面的代码new Promise的简单形式:

newPromise(function(resolve,reject){    reject(newError("我错了,请原谅俺!!"));});

下面我们来综合看看使用resolve方法和reject方法的demo如下:

上面的代码的含义是给testPromise方法传递一个参数,返回一个promise对象,如果为true的话,那么调用promise对象中的resolve()方法,并且把其中的参数传递给后面的then第一个函数内,因此打印出 “hello world”, 如果为false的话,会调用promise对象中的reject()方法,则会进入then的第二个函数内,会打印No thanks;

理解Promise异步调用的操作

varpromise =newPromise(function(resolve){console.log(1);    resolve(3);});promise.then(function(value){console.log(value);});console.log(2);

上面的代码输出我们可以看到,分别为 1,2,3; 首先代码从上往下执行,首先输出1,然后调用resolve(3)这个方法,这时候promise对象变为确定状态,即调用onFulFilled这个方法,从上面了解到,resolve(成功) 时 调用onFulfilled 方法,Promise.then 成功和失败时都可以使用,因此第一个函数是成功调用的,但是Promise对象是以异步方式调用的,所以先执行console.log(2),输出的是2,然后输出的是3;

理解是同步调用还是异步调用

如上代码;如果在调用ready()方法之前DOM已经载入完成的话,就会对回调函数进行同步调用,先输出DOM Load Success 后输出 我是同步输出的 文案;如果在调用ready()方法之前DOM为未载入完成的话,那么代码先会执行 window.addEventListener(‘DOMContentLoaded’, fn);

就会异步调用该函数,那么就会先输出 “我是同步输出的”,后输出”DOM Load Success”;

为了解决上面的同步或者异步混乱的问题,我们现在可以使用promise对象使用异步的方式来解决;如下代码

functionreadyPromise(){returnnewPromise(function(resolve,reject){varreadyState =document.readyState;if(readyState ==='interactive'|| readyState ==='complete') {            resolve();        }else{window.addEventListener('DOMContentLoaded', resolve);        }    });}readyPromise().then(function(){console.log("DOM Load Success");});console.log("我是同步加载的,先执行我");

输出如下:先输出”我是同步加载的,先执行我” 后输出 “DOM Load Success”。因为promise对象是异步加载的。

理解promise的三种状态

Promise 对象有三种状态:

Resolve 可以理解为成功的状态;

Rejected 可以理解为失败的状态;

Pending既不是Resolve也不是Rejected状态;可以理解为Promise对象实例创建时候的初始状态;

比如Promise对象中的resolve方法就是调用then对象的第一个函数,也就是成功的状态;而reject方法就是调用then对象的第二个函数,也就是失败的状态;

理解then()

上面的代码,比如如下这样的代码就是then的列子;代码如下:

functiontestPromise(ready){returnnewPromise(function(resolve,reject){if(ready) {            resolve("hello world");        }else{            reject("No thanks");        }    });};// 方法调用testPromise(true).then(function(msg){console.log(msg);},function(error){console.log(error);});

上面的代码就是利用了 then(onFulfilled,onRejected)方法来执行的,第一个方法就是成功状态的标志,第二个方法是失败的状态标志;

当然在多个任务的情况下then方法同样可以使用;比如上面的代码改成如下:

functiontestPromise(ready){returnnewPromise(function(resolve,reject){if(ready) {            resolve("hello world");        }else{            reject("No thanks");        }    });};// 方法调用testPromise(true).then(function(msg){console.log(msg);}).then(testPromise2)  .then(testPromise3);functiontestPromise2(){console.log(2);}functiontestPromise3(){console.log(3);}

输出如下:hello world ,2,3

上面的代码是then的链式调用方式,输出是按顺序输出的 分别为 hello world , 2,3; 使用链式调用的原因是 每次调用后都会返回promise对象;

理解Promise.catch()方法

Promise.catch()方法是promise.then(undefined,onRejected)方法的一个别名,该方法用来注册当promise对象状态变为Rejected的回调函数。

如下代码:

varpromise =Promise.reject(newError("message"));promise.catch(function(error){console.log(error);});

打印如下所示:

image

理解每次调用then都会返回一个新创建的promise对象

不管是then还是catch方法调用,都返回一个新的promise对象;

下面我们来看看代码如下:

varpromise1 =newPromise(function(resolve){    resolve(1);});varthenPromise = promise1.then(function(value){console.log(value);});varcatchPromise = thenPromise.catch(function(error){console.log(error);});console.log(promise1 !== thenPromise);// trueconsole.log(thenPromise !== catchPromise);//true

如上代码,打印的都是true,这说明不管是then还是catch都返回了和新创建的promise是不同的对象;

如果我们知道了then方法每次都会创建返回一个新的promise对象的话,那么久不难理解下面的代码了;如下:

varpromise1 =newPromise(function(resolve){    resolve(1);});promise1.then(function(value){returnvalue *2;});promise1.then(function(value){returnvalue *2;});promise1.then(function(value){console.log("1"+value);});

如上的代码;打印出11;因为他们每次调用then方法时,是使用的不同的promise对象;因此最后打印的value还是1;但是如果我们then方法是连续调用的话,那情况就不一样了,比如如下代码:

varpromise1 =newPromise(function(resolve){    resolve(2);});promise1.then(function(value){returnvalue *2;}).then(function(value){returnvalue *2;}).then(function(value){console.log("1"+value);});

打印出18,即 “1” + 222 = 18;

上面第一种方法没有使用方法链的调用,上面第一种那种写法then 调用几乎是同时开始进行的,且传给每个then的value都是1;

第二种方式是使用方法链的then,使多个then方法连接在一起了,因此函数会严格执行 resolve – then — then – then的顺序执行,并且传递每个then方法的value的值都是前一个promise对象中return的值;因此最后的结果就是18了;

现在我们再回过头一刚开始我们讨论的为什么要使用promise的原因的问题了,比如2个ajax请求,后一个ajax请求需要获取到前一个ajax请求的数据,我们之前在使用jquery写代码是如下的:

$.ajax({url:'',dataType:'json',success:function(data){// 获取data数据 传给下一个请求varid = data.id;    $.ajax({url:'',data:{"id":id},success:function(){// .....}    });  }});

现在我们学习了then方法后,我们可以重新编写上面的代码变成如下:

varajaxPromise =newPromise(function(resolve){    resolve();});ajaxPromise.then(function(){    $.ajax({url:'',dataType:'json',success:function(data){varid = data.id;returnid;        }    })}).then(function(id){    $.ajax({url:'',dataType:'json',data:{"id":id},success:function(data){console.log(data);        }    })});

理解Promise.all

Promise.all可以接受一个元素为Promise对象的数组作为参数,当这个数组里面所有的promise对象都变为resolve时,该方法才会返回。

如下代码:

varpromise1 =newPromise(function(resolve){    setTimeout(function(){        resolve(1);    },3000);});varpromise2 =newPromise(function(resolve){    setTimeout(function(){        resolve(2);    },1000);});Promise.all([promise1,promise2]).then(function(value){console.log(value);// 打印[1,2]});

如上代码 打印的是[1,2]; 如上我们看到promise1对象中的setTimeout是3秒的时间,而promise2对象中的setTimeout是1秒的时间,但是在Promise.all方法中会按照数组的原先顺序将结果返回;

在我们平时的需求中,或许有这种情况的需求,比如我们需要发2个ajax请求时,不管他们的先后顺序,当这2个ajax请求都同时成功后,我们需要执行某些操作的情况下,这种情况非常适合;

理解Promise.race

如上可知:Promise.all 在接收到的所有对象promise都变为FulFilled或者 Rejected状态之后才会继续后面的处理,但是Promise.race的含义是只要有一个promise对象进入FulFilled或者Rejected状态的话,程序就会停止,且会继续后面的处理逻辑;

如下代码:

// `delay`毫秒后执行resolvefunctiontimerPromise(delay){returnnewPromise(function(resolve){        setTimeout(function(){            resolve(delay);        },delay);    });}// 任何一个promise变为resolve或reject 的话程序就停止运行Promise.race([    timerPromise(1),    timerPromise(32),    timerPromise(64),    timerPromise(128)]).then(function(value){console.log(value);// => 1});

如上代码创建了4个promise对象,这些promise对象分别在1ms,32ms,64ms,128ms后变为确定状态,并且在第一个变为确定状态后1ms后,then函数就会被调用,这时候resolve()方法给传递的值为1,因此执行then的回调函数后,值变为1;

我们再来看看当一个promise对象变为确定状态(FulFiled)的时候,他们后面的promise对象是否还在运行呢?我们继续看如下代码运行:

varrunPromise =newPromise(function(resolve){    setTimeout(function(){console.log(1);        resolve(2);    },500);});varrunPromise2 =newPromise(function(resolve){    setTimeout(function(){console.log(3);        resolve(4);    },1000);});// 第一个promise变为resolve后程序停止Promise.race([runPromise,runPromise2]).then(function(value){console.log(value);});

如上代码是使用定时器调用的,上面是2个promise对象,我们看到第一个promise对象过500毫秒后加入到执行队列里面去,如果执行队列没有其他线程在运行的时候,就执行该定时器,所以第一次打印1,然后调用resolve(2); 接着调用promise.race方法,该方法只要有一个变为成功状态(FulFiled)的时候,程序就会停止,因此打印出2,同时后面的promise对象接着执行,因此打印出3,但是由于promise.race()该方法已经停止调用了,所以resolve(4)不会有任何输出;因此最后输出的是1,2,3;

由此我们得出结论,当一个promise对象变为(FulFilled)成功状态的时候,后面的promise对象并没有停止运行。

Deferred和Promise的关系

Deferred 包含 Promise;

Deferred具备Promise的状态进行操作的特权方法;

下面我们来看看使用promise来实现deferred;如下代码:

functionDeferred(){this.promise =newPromise(function(resolve,reject){this._resolve = resolve;this._reject = reject;    }.bind(this));}Deferred.prototype.resolve =function(value){this._resolve.call(this.promise,value);};Deferred.prototype.reject =function(reason){this._reject.call(this.promise,reason);};functiongetURL(URL){vardeferred =newDeferred();varreq =newXMLHttpRequest();    req.open('GET',URL,true);    req.onload =function(){if(req.status ===200) {            deferred.resolve(req.responseText);        }else{            deferred.reject(newError(req.statusText));        }    };    req.onerror =function(){        deferred.reject(newError(req.statusText));    };    req.send();returndeferred.promise;}varURL ='http://127.0.0.1/promise/promise.php';getURL(URL).then(functiononFulfilled(value){console.log(value);});

其中promise.php代码输出的是一个json的数据,代码如下:

<?php$data = json_decode(file_get_contents("php://input"));    header("Content-Type: application/json; charset=utf-8");echo('{"id" : '. $data->id .', "age" : 24, "sex" : "boy", "name" : "huangxueming"}');?>

最后执行打印console的出来是:

{“id” : , “age” :24, “sex” : “boy”, “name” : “huangxueming”}

使用promise封装deferred的方法,无非就是使用promise对象中的resolve和Reject等调用方法,下面我们再来看看使用promise对象对ajax请求的封装如下:

functiongetURL(URL){returnnewPromise(function(resolve, reject){varreq =newXMLHttpRequest();        req.open('GET', URL,true);        req.onload =function(){if(req.status ===200) {                resolve(req.responseText);            }else{                reject(newError(req.statusText));            }        };        req.onerror =function(){            reject(newError(req.statusText));        };        req.send();    });}varURL ='http://127.0.0.1/promise/promise.php';getURL(URL).then(functiononFulfilled(value){console.log(value);});

上面分别两种方式使用promise对象实现ajax请求的封装对比如下:

Deferred那种方式不需要将promise代码括起来。

Promise代表了一个对象,这个对象的状态现在还不确定,但是未来一个时间点它的状态要么变为正常值(FulFilled),要么变为异常值(Rejected);而Deferred对象表示了一个处理还没有结束的这种事实,在它的处理结束的时候,可以通过Promise来取得处理结果。

作者:紫陌兰溪

链接:https://www.jianshu.com/p/1ec8d1c4e287

来源:简书

简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • Promise是一个构造函数,自己身上有all、reject、resolve这几个眼熟的方法,原型上有then、c...
    养猫废心养狗废人阅读 204评论 0 1
  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,698评论 1 56
  • 前言 本文旨在简单讲解一下javascript中的Promise对象的概念,特性与简单的使用方法。并在文末会附上一...
    _暮雨清秋_阅读 2,180评论 0 3
  • 一、Promise的含义 Promise在JavaScript语言中早有实现,ES6将其写进了语言标准,统一了用法...
    Alex灌汤猫阅读 817评论 0 2
  • 在ES6当中添加了很多新的API其中很值得一提的当然少不了Promise,因为Promise的出现,很轻松的就给开...
    嘿_那个谁阅读 3,662评论 2 3