欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

本文由前端林子发表于云+社区专栏

Promise是CommonJS提出的一种规范,在ES6中已经原生支持Promise对象,非ES6环境可以用Bluebird等库来支持。

0.引入

在js中任务的执行模型有两种:同步模式和异步模式。

同步模式:后一个任务B等待前一个任务A结束后,再执行。任务的执行顺序和任务的排序顺序是一致的。

异步模式:每一个任务有一个或多个回调函数,前一个任务A结束后,不是执行后一个任务B,而是执行任务A的回调函数。而后一个任务B是不等任务A结束就执行。任务的执行顺序,与任务的排序顺序不一致。

异步模式编程有四种方法:回调函数(最基本的方法,把B写成A的回调函数)、事件监听(为A绑定事件,当A发生某个事件,就执行B)、发布/订阅,以及本文要介绍的Promise对象。

Promise是一个用于处理异步操作的对象,可以将回调函数写成链式调用的写法,让代码更优雅、流程更加清晰,让我们可以更合理、更规范地进行异步处理操作。它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。

1.Promise的基本知识

1.1 三种状态

Pending:进行中,刚创建一个Promise实例时,表示初始状态;

resolved(fulfilled):resolve方法调用的时候,表示操作成功,已经完成;

Rejected:reject方法调用的时候,表示操作失败;

1.2 两个过程

这三种状态只能从pendeng-->resolved(fulfilled),或者pending-->rejected,不能逆向转换,也不能在resolved(fulfilled)和rejected之间转换。并且一旦状态改变,就不会再改变,会一直保持这个结果。

汇总上述,创建一个Promise的实例是这样的:

//创建promise的实例
let promise = new Promise((resolve,reject)=>{
//刚创建实例时的状态:pending if('异步操作成功'){
//调用resolve方法,状态从pending变为fulfilled
resolve();
}else{
//调用reject方法,状态从pending变为rejected
reject();
}
});

1.3 then()

用于绑定处理操作后的处理程序,分别指定fulfilled状态和rejected状态的回调函数,即它的参数是两个函数,第一个用于处理操作成功后的业务,第二个用于处理操作失败后的业务。

//then()
promise.then((res)=> {
//处理操作成功后的业务(即Promise对象的状态变为fullfilled时调用)
},(error)=> {
//处理操作失败后的业务(即Promise对象的状态变为rejected时调用)
});

1.4 catch()

用于处理操作异常的程序,catch()只接受一个参数

//catch()
promise.catch((error)=> {
//处理操作失败后的业务
});

一般来说,建议不要在then()里面定义rejected状态的回调函数,而是将then()用于处理操作成功,将catch()用于处理操作异常。因为这样做可以捕获then()执行中的错误,也更接近同步中try/catch的写法:

//try-catch
// bad
promise.then((res)=> {
//处理操作成功后的业务
}, (error)=> {
//处理操作失败后的业务
}); // good
promise
.then((res)=> {
//处理操作成功后的业务
})
.catch((error)=> {
//处理操作失败后的业务
});

1.5 all()

接受一个数组作为参数,数组的元素是Promise实例对象。只有当参数中的实例对象的状态都为fulfilled时,Promise.all( )才会有返回。

实例代码(可直接在浏览器中打开):

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<title>Promise实例</title>
<style type="text/css"></style>
<script type="text/javascript">
window.onload = () => {
//创建实例promise1
let promise1 = new Promise((resolve) => {
setTimeout(() => {
resolve('promise1操作成功');
console.log('1')
}, 3000);
}); //创建实例promise1
let promise2 = new Promise((resolve) => {
setTimeout(() => {
resolve('promise1操作成功');
console.log('2')
}, 1000);
}); Promise.all([promise1, promise2]).then((result) => {
console.log(result);
});
}
</script>
</head> <body>
<div></div>
</body> </html>

结果(注意看时间):

Promise.all()

代码说明:

1s后,promise2进入fulfilled状态,间隔2s,也就是3s后,promise1也进入fulfilled状态。这时,由于两个实例都进入了fulfilled状态,所以Promise.all()才进入了then方法。

使用场景:执行某个操作需要依赖多个接口请求回的数据,且这些接口之间不存在互相依赖的关系。这时使用Promise.all(),等到所有接口都请求成功了,它才会进行操作。

1.6 race()

和all()的参数一样,参数中的promise实例,只要有一个状态发生变化(不管是成功fulfilled还是异常rejected),它就会有返回,其他实例中再发生变化,它也不管了。

实例代码(可直接在浏览器中打开):

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<title>Promise实例</title>
<style type="text/css"></style>
<script type="text/javascript">
window.onload = () => {
//创建实例promise1
let promise1 = new Promise((resolve) => {
setTimeout(() => {
resolve('promise1操作成功');
console.log('1')
}, 3000);
}); //创建实例promise1
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('promise1操作失败');
console.log('2')
}, 1000);
}); Promise.race([promise1, promise2])
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
})
}
</script>
</head> <body>
<div></div>
</body> </html>

结果(注意看时间):

Promise.race()

代码说明:

1s后,promise2进入rejected状态,由于一个实例的状态发生了变化,所以Promise.race()就立刻执行了。

2 实例

平时开发中可能经常会遇到的问题是,要用ajax进行多次请求。例如现在有三个请求,请求A、请求B、请求C。请求C要将请求B的请求回来的数据做为参数,请求B要将请求A的请求回来的数据做为参数。

按照这个思路,我们可能会直接写出这样的层层嵌套的代码:

//------请求A 开始---------
$.ajax({
success:function(res1){ //------请求B 开始----
$.ajax({
success:function(res2){ //----请求C 开始---
$.ajax({
success:function(res3){
}
});
//---请求C 结束--- }
});
//------请求B 结束----- }
});
//------请求A 结束---------

在请求A的success后,请求B发送请求,在请求B 的success后,请求C发送请求。请求C结束后,再向上到请求B结束,请求B结束后,再向上到请求A结束。

这样虽然可以完成任务,但是代码层层嵌套,代码可读性差,也不便于调试和后续的代码维护。而如果用Promise,你可以这样写(示意代码,无ajax请求):

此处附上完整可执行代码,可在浏览器的控制台中查看执行结果:

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<title>Promise实例</title>
<style type="text/css"></style>
<script type="text/javascript">
window.onload = () => {
let promise = new Promise((resolve, reject) => { if (true) {
//调用操作成功方法
resolve('操作成功');
} else {
//调用操作异常方法
reject('操作异常');
}
}); //then处理操作成功,catch处理操作异常
promise.then(requestA)
.then(requestB)
.then(requestC)
.catch(requestError); function requestA() {
console.log('请求A成功');
return '下一个是请求B';
}
function requestB(res) {
console.log('上一步的结果:' + res);
console.log('请求B成功');
return '下一个是请求C';
}
function requestC(res) {
console.log('上一步的结果:' + res);
console.log('请求C成功');
}
function requestError() {
console.log('请求失败');
}
}
</script>
</head> <body>
<div></div>
</body> </html>

结果如下:

实例

可以看出请求C依赖请求B的结果,请求B依赖请求A的结果,在请求A中是使用了return将需要的数据返回,传递给下一个then()中的请求B,实现了参数的传递。同理,请求B中也是用了return,将参数传递给了请求C。

3.小结

本文主要介绍了Promise对象的三个状态和两个过程。“三个状态”是:初始化、操作成功、操作异常,“两个过程”是初始化状态到操作成功状态,和初始化状态到操作异常状态。除此之前,还有两种实例方法:then()、catch()来绑定处理程序。类方法:Promise.all()、Promise.race()。如有问题,欢迎指正。

相关阅读

【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识

此文已由作者授权腾讯云+社区发布,更多原文请点击

搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!

海量技术实践经验,尽在云加社区

这次聊聊Promise对象的更多相关文章

  1. JavaScript异步编程(1)- ECMAScript 6的Promise对象

    JavaScript的Callback机制深入人心.而ECMAScript的世界同样充斥的各种异步操作(异步IO.setTimeout等).异步和Callback的搭载很容易就衍生"回调金字 ...

  2. ES6中Promise对象个人理解

    Promise是ES6原生提供的一个用来传递异步消息的对象.它减少了传统ajax金字塔回调,可以将异步操作以同步操作的流程表达出来使得代码维护和可读性方面好很多. Promise的状态: 既然是用来传 ...

  3. 教你如何使用ES6的Promise对象

    教你如何使用ES6的Promise对象 Promise对象,ES6新增的一个全新特性,这个是 ES6中非常重要的一个对象 Promise的设计初衷 首先,我们先一起了解一下,为什么要设计出这么一个玩意 ...

  4. angular学习笔记(二十八-附2)-$http,$resource中的promise对象

    下面这种promise的用法,我从第一篇$http笔记到$resource笔记中,一直都有用到: HttpREST.factory('cardResource',function($resource) ...

  5. ES6深入学习记录(二)promise对象相关

    1.Promise的含义 Promise是异步编程的一种解决方案,比传统的解决方案--回调函数和事件更合理和强大.ES6将其写进了语言标准,统一了用法,原生提供了promise对象. 所谓Promis ...

  6. es6中的promise对象

    Promise是异步里面的一种解决方案,解决了回调嵌套的问题,es6将其进行了语言标准,同意了用法,提供了`promise`对象, promise对象有三种状态:pending(进行中) .Resol ...

  7. ES6的promise对象应该这样用

    ES6修补了一位Js修真者诸多的遗憾. 曾几何时,我这个小白从js非阻塞特性的坑中爬出来,当我经历了一些回调丑陋的写法和优化的尝试之后,我深深觉得js对于多线程阻塞式的开发语言而言,可能有着其太明显的 ...

  8. Angularjs promise对象解析

    1.先来看一段Demo,看完这个demo你可以思考下如果使用$.ajax如何处理同样的逻辑,使用ng的promise有何优势? var ngApp=angular.module('ngApp',[]) ...

  9. Promise对象

    1.Promise思想:每一个异步任务立刻返回一个Promise对象,由于是立刻返回,所以可以采用同步操作的流程.这个Promises对象有一个then方法,允许指定回调函数,在异步任务完成后调用. ...

随机推荐

  1. Loadrunner登录判断

    loadrunner判断登录是否成功,以下方法可以解决:1.利用添加检查点web_reg_find函数的方法---------------------------- lr_start_transact ...

  2. AndroidStudio 3 export jar file

    1.  File -> New -> New Module -> Android Library  例子中暂命名ModuleA 2. 修改刚创建 ModuleA下的build.gra ...

  3. 别人的Linux私房菜(5)首次CentOS7与帮助等

    ctrl alt F1-F6切换终端tty1-6,其中,F1的终端带有用户界面. 在终端登录后,输入startx启动个人图形界面.(启动有一些条件限制,如没有其他的X Window启用,已经安装,并具 ...

  4. poj 3279(暴力)

    题意:有一个n*m的格子,每个格子都有黑白两面(0表示白色,1表示黑色).我们需要把所有的格子都反转成黑色,每反转一个格子,它上下左右的格子都会跟着反转.请求出用最小步数完成反转时每个格子反转的次数. ...

  5. python open()函数的模式选择

    python open()函数打开文件的模式详解 使用python处理文件时,避免不了要用到open()函数.我们今天主要讨论mode参数的区分. fd = open('文件名(路径)’, mode= ...

  6. Python开发——6.文件操作

    一.文件操作 1.文件操作的处理流程 打开文件得到文件句柄并赋值给一个变量====>通过句柄对文件进行分析====>关闭文件 #1. 打开文件,得到文件句柄并赋值给一个变量 f=open( ...

  7. Java ,python面向对象的继承及其区别

    JAVA JAVA继承基本样式 class Demo extends Object{ Demo(int a){ this(); } Demo(){ super(); } } java默认继承Objec ...

  8. linux下使用eclipse打开esp32工程文件,并进行编译下载。

    在之前的文章中,已经讲过将esp-idf 运用linux自带的工具下载,但是为了开发的系统性,这里运用eclipse来对idf进行整体的编译. 首先 cd /esp32 运行eclipse,将work ...

  9. 迁移桌面程序到MS Store(1)——通过Visual Studio创建Packaging工程

    之前跑去做了一年多的iOS开发,被XCode恶心得不行.做人呢,最重要的是开心.所以我就炒了公司鱿鱼,挪了个窝回头去做Windows开发了.        UWP什么的很久没有正儿八经写了,国内的需求 ...

  10. WPF 开机注册自启动及设置确保以管理员运行

    最近用到开机自启动设置,在此记录下设置自启动的步骤,如果对各位有需要可以借阅,之前设置管理权限解释在创建解决方案是将VS以管理员方式打开,这种方法可以实现,但是个人还是偏向与本文的方式,不多说啥了,下 ...