ES6 中 Promise 详解

博客地址:https://ainyi.com/16

Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点:

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

简单来说,Promise 就是用同步的方式写异步的代码,用来解决回调问题

then()方法

then 方法就是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。

而 Promise 的优势就在于这个链式调用。我们可以在 then 方法中继续写 Promise 对象并返回,然后继续调用 then 来进行回调操作。

可有两个参数,第一个是成功 resolve 调用的方法,第二个是失败 reject 调用的方法

下面做一个买笔写作业上交的演示,它们是层层依赖的关系,下一步的的操作需要使用上一部操作的结果。(这里使用 setTimeout 模拟异步操作),正式开发可以用 ajax 异步

 1         //买笔
 2         function buy(){
 3             console.log("开始买笔");
 4             var p = new Promise(function(resolve,reject){
 5                 setTimeout(function(){
 6                     console.log("买了笔芯");
 7                     resolve("数学作业");
 8                 },1000);
 9             });
10             return p;
11         }
12         //写作业
13         function work(data){
14             console.log("开始写作业:"+data);
15             var p = new Promise(function(resolve,reject){
16                 setTimeout(function(){
17                     console.log("写完作业");
18                     resolve("作业本");
19                 },1000);
20             });
21             return p;
22         }
23 
24         function out(data){
25             console.log("开始上交:"+data);
26             var p = new Promise(function(resolve,reject){
27                 setTimeout(function(){
28                     console.log("上交完毕");
29                     resolve("得分:A");
30                 },1000);
31             });
32             return p;
33         }
34         /* 不建议使用这种方式
35         buy().then(function(data){
36             return work(data);
37         }).then(function(data){
38             return out(data);
39         }).then(function(data){
40             console.log(data);
41         });*/
42 
43         //推荐这种简化的写法
44         buy().then(work).then(out).then(function(data){
45             console.log(data);
46         });

正式开发用ajax异步:

 1         var promise = new Promise(function(resolve,reject){
 2             $.ajax({
 3                 url:'/api/poisearch.json',
 4                 method:'get',
 5                 datatype:'json',
 6                 success:(res) =>{
 7                     resolve(res)
 8                 },
 9                 error:(err)=>{
10                     reject(err)
11                 }
12             });
13         });
14 
15         promise.then(function(res){
16             return res.data
17         }).then(function(data){
18             return data.result;
19         }).then(function(result){
20             console.log(result)
21         });
22 
23         //推荐使用箭头函数简写成,极大提升了代码的简洁性和可读性
24         promise.then(res => res.data).then(data => data.result).then(result => console.log(result));

reject()方法:

上面样例我们通过 resolve 方法把 Promise 的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。

而 reject 方法就是把 Promise 的状态置为已失败(Rejected),这时 then 方法执行“失败”情况的回调(then 方法的第二参数)

 1         function rebuy(){
 2             console.log("开始买笔");
 3             var p = new Promise(function(resolve,reject){
 4                 setTimeout(function(){
 5                     console.log("买笔失败");
 6                     reject("没带够钱");
 7                 },1000);
 8             });
 9             return p;
10         }
11 
12         function rework(data){
13             console.log("开始写作业:"+data);
14             var p = new Promise(function(resolve,reject){
15                 setTimeout(function(){
16                     console.log("写完作业");
17                     resolve("作业本");
18                 },1000);
19             });
20             return p;
21         }
22         
23         rebuy().then(rework,function(data){
24             console.log(data);
25         });

catch()方法:

1. 它可以和 then 的第二个参数一样,用来指定 reject 的回调

 1        function rebuy(){
 2             console.log("开始买笔");
 3             var p = new Promise(function(resolve,reject){
 4                 setTimeout(function(){
 5                     console.log("买笔失败");
 6                     reject("没带够钱");
 7                 },1000);
 8             });
 9             return p;
10         }
11 
12         function rework(data){
13             console.log("开始写作业:"+data);
14             var p = new Promise(function(resolve,reject){
15                 setTimeout(function(){
16                     console.log("写完作业");
17                     resolve("作业本");
18                 },1000);
19             });
20             return p;
21         }
22 
23         rebuy().then(rework).catch(function(data){
24             console.log(data);
25         });    

2. 它的另一个作用是,当执行 resolve 的回调(也就是上面 then 中的第一个参数)时,如果抛出异常了(代码出错了),那么也不会报错卡死 js,而是会进到这个 catch 方法中。

 1         function buy(){
 2             console.log("开始买笔");
 3             var p = new Promise(function(resolve,reject){
 4                 setTimeout(function(){
 5                     console.log("买了笔芯");
 6                     resolve("数学作业");
 7                 },1000);
 8             });
 9             return p;
10         }
11 
12         function work(data){
13             console.log("开始写作业:"+data);
14             var p = new Promise(function(resolve,reject){
15                 setTimeout(function(){
16                     console.log("写完作业");
17                     resolve("作业本");
18                 },1000);
19             });
20             return p;
21         }
22 
23         buy().then(function(data){
24             throw new Error("买了坏的笔芯");
25             work(data);
26         }).catch(function(data){
27             console.log(data);
28         });

all()方法:

Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。

比如下面代码,两个个异步操作是并行执行的,等到它们都执行完后才会进到 then 里面。同时 all 会把所有异步操作的结果放进一个数组中传给 then。

 1         //买作业本
 2         function cutUp(){
 3             console.log('挑作业本');
 4             var p = new Promise(function(resolve, reject){ //做一些异步操作
 5                 setTimeout(function(){
 6                     console.log('挑好购买作业本');
 7                     resolve('新的作业本');
 8                 }, 1000);
 9             });
10             return p;
11         }
12          
13         //买笔
14         function boil(){
15             console.log('挑笔芯');
16             var p = new Promise(function(resolve, reject){ //做一些异步操作
17                 setTimeout(function(){
18                     console.log('挑好购买笔芯');
19                     resolve('新的笔芯');
20                 }, 1000);
21             });
22             return p;
23         }
24 
25         Promise.all([cutUp(),boil()]).then(function(results){
26             console.log("写作业的工具都买好了");
27             console.log(results);
28         });

race()方法:

race 按字面解释,就是赛跑的意思。race 的用法与 all 一样,只不过 all 是等所有异步操作都执行完毕后才执行 then 回调。而 race 的话只要有一个异步操作执行完毕,就立刻执行 then 回调。

注意:其它没有执行完毕的异步操作仍然会继续执行,而不是停止。

这里我们将上面样例的 all 改成 race

1         Promise.race([cutUp(), boil()]).then(function(results){
2             console.log("哈哈,我先买好啦");
3             console.log(results);
4         });

race 使用场景很多。比如我们可以用 race 给某个异步请求设置超时时间,并且在超时后执行相应的操作。

请求某个图片资源

 1        function requestImg(){
 2             var p = new Promise(function(resolve, reject){
 3             var img = new Image();
 4             img.onload = function(){
 5                resolve(img);
 6             }
 7             img.src = 'xxxxxx';
 8             });
 9             return p;
10         }
11          
12         //延时函数,用于给请求计时
13         function timeout(){
14             var p = new Promise(function(resolve, reject){
15                 setTimeout(function(){
16                     reject('图片请求超时');
17                 }, 5000);
18             });
19             return p;
20         }
21          
22         Promise.race([requestImg(), timeout()]).then(function(results){
23             console.log(results);
24         }).catch(function(reason){
25             console.log(reason);
26         });
27         //上面代码 requestImg 函数异步请求一张图片,timeout 函数是一个延时 5 秒的异步操作。我们将它们一起放在 race 中赛跑。
28         //如果 5 秒内图片请求成功那么便进入 then 方法,执行正常的流程。
29         //如果 5 秒钟图片还未成功返回,那么则进入 catch,报“图片请求超时”的信息。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏性能与架构

快速了解 YAML

什么是 YAML? YAML 是一个数据序列化的标准,适用于所有开发语言,最大的特点是可读性好 YAML 的一个主要应用方向就是编写配置文件,有非常多的系统和框...

4045
来自专栏帅小子的日常

freemarker的使用

1442
来自专栏对角另一面

读Zepto源码之Callbacks模块

Callbacks 模块并不是必备的模块,其作用是管理回调函数,为 Defferred 模块提供支持,Defferred 模块又为 Ajax 模块的 promi...

2220
来自专栏Django Scrapy

Django安装及简单使用 1.2

Django安装及简单使用 1.2 代码都在github: URL:https://github.com/njxshr/codes/tree/master/...

3248
来自专栏开发与安全

《鸟哥的linux私房菜》基本命令笔记

1.以前没注意过的,略写的命令option后面只能空格后加参数,而标准option即可以空格也可以等号后跟着参数,如date命令,date -r filenam...

2446
来自专栏Dawnzhang的开发者手册

easyui 进阶之表单校验、自定义校验

easyui是一种基于jQuery的用户界面插件集合,它为创建现代化,互动,JavaScript应用程序,提供必要的功能,完美支持HTML5网页的完整框架,节省...

1672
来自专栏黒之染开发日记

对less的理解

less是写css时可以采用的另一种写法,用less的格式写出来的东西,可以通过编译器编译成css。也就是可以使用某种方法,把less文件变成css文件。编译成...

1912
来自专栏GreenLeaves

Jquery filter()方法简介

利用filter函数可以从wrapper set中过滤符合条件的dom元素。 如下图html代码,假如我们要获取类名为filter的<a>标签,用filter方...

2089
来自专栏编程

前端调试必备:CHROME CONSOLE控制台的使用:诊断并记录

chrome console控制台日志记录是检查您的页面或应用程序的功能的强大方法。 我们从console.log()开始,探索其他高级用法。 这篇文章主要讲以...

23110
来自专栏对角另一面

lodash源码分析之List缓存

昨日我沿着河岸/漫步到/芦苇弯腰喝水的地方 顺便请烟囱/在天空为我写一封长长的信 潦是潦草了些/而我的心意/则明亮亦如你窗前的烛光/稍有暧昧之处/势所难免...

2776

扫码关注云+社区

领取腾讯云代金券