Promise对象
目录1. Promise简介
-
Promise是异步编程的一种解决方案。从语法上来说,Promise是一个对象,里面保存着某个未来才会结束的事件的结果,从这个对象中可以获取异步操作的信息
-
Promise具有两个特点:
-
对象的状态不受外界影响
Promise对象只有三种状态:Pending(进行中)、Fulfilled(已成功)、Rejected(已失败)
只有异步操作的结果可以决定当前是哪一种状态,其它任何操作都无法改变这个状态
-
一旦状态改变就不会再变,且任何时候都可以等到这个结果
Promise对象的状态只有两种可能:Pending→Fulfilled或Pending→Rejected。只要这两种情况发生,状态就不会再改变,此时就称为Resolved(已定型)。一旦定型,无论何时添加回调函数都会得到这个结果
-
-
Promise的优点
- 将异步操作以同步操作的流程表达出来,避免回调函数的层层嵌套
- 统一了操作接口,使得控制异步操作更容易
-
Promise的缺点
- Promise无法被取消,一旦新建就会立即执行,无法中止
- 如果不设置回调函数,Promise内部抛出的错误不会反应到外部
- 当处于Pending状态时,无法得知目前进展到哪个阶段
2. Promise的使用
2.1 创建Promise对象
-
Promise对象是一个构造函数,用来生成Promise实例
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是Resolve函数(Pending→Resolved时触发)和Reject函数(Pending→Rejected时触发),其中Reject函数可省略
Promise构造函数中的Resolve和Reject仅作声明用,后续需要在Promise实例生成后,使用then方法分别制定两者的回调函数
-
一个完整的Promise实例创建流程
-
在Promise构造函数中设置触发条件
var promise = new Promise(function(resolve,reject){ //此处无需拘泥于if-else,任何可以是任何用于判断执行是否成功失败的函数都可以 //甚至可以直接书写resolve,不需要判断 if(/*异步操作成功*/){ resolve(value) }else{ reject(error); } })
-
在Promise实例创建后使用then设定具体回调函数
promise.then( function(value){ //执行成功后具体操作 }, function(error){ //执行失败后具体操作 } )
-
2.2 Promise的执行时间
-
Promise在新建后就会立刻执行,而then方法指定的回调函数将在当前脚本所有同步任务执行完毕后才会执行
let promise = new Promise(function(resolve,reject){ console.log("Promise"); resolve(); }) promise.then(function(){ console.log('Resolved'); }); console.log('Hi!'); //Promise //Hi! //Resolved
-
解析:promise在创建完后立刻执行,所以首先输出的是Promise,然后按照脚本执行顺序输出Hi!,then方法中的函数会在脚本中所有同步任务执行完毕后执行,所以最后才会输出Resolved
2.3 简写形式
-
由于reject函数可以被省略,所以更常见的是下面的简写形式
function timeout(ms){ return new Promise((resolve,reject) => { setTimeout(resolve,ms,'done'); }); } timeout(100).then((value)=>{ console.log(value); });
-
解析:在调用timeout函数的时候就会新建一个promise对象实例,随后延迟函数setTimeout就会立刻被执行,并在100ms后输出'done'这一内容。此处在新建Promise对象的时候,并没有任何判断,默认setTimeout执行即触发resolve,同理then方法也并没有考虑Reject的情况,value直接传递给resolve处理
3. then方法
-
then方法是定义在Promise的原型对象Promise.prototype上的。then方法的作用是为Promise实例添加状态改变时的回调函数。then方法的第一个参数是Resolved状态的回调函数,第二个参数是Rejected状态的回调函数,Rejected可省略
-
then方法实际上返回的是一个新的Promise实例,这意味着可以使用链式写法,后一个then的回调函数将会在前一个then返回的Promise对象执行完毕后再执行
getJSON("/post/1.json").then(function(post){ return getJSON(post.commentURL); }).then(function funcA(comments){ console.log("Resolved:", comments); },function funcB(err){ console.log("Rejected: ",err); });
4. catch方法
-
catch方法是
.then(null,rejection)
的别名,不难看出catch实际上就是then只考虑了rejected(执行失败)的情况。catch用于指定发生错误的回调函数getJSON("/post/1.json").then(function(post){ //... }).catch(function(error){ //处理getJSON和前一个回调函数的错误 console.log('错误!',error) });
-
注意事项
- 之所以要使用catch是因为,如果不使用catch指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应
- Promise对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。即错误总是会被下一个catch所捕获
- 如果Promise状态已经变为Resolved,再抛出错误是无效的。在resolve语句后面抛出的错误,不会再被捕获
5. all方法和race方法
-
Promise.all()
和Promise.race()
方法都是用于将多个Promise实例包装成一个新的Promise实例var p = Promise.all([p1,p2,p3]); var p = Promise.race([p1,p2,p3])
-
Promise.all()
有些类似"与"(&&)操作,即包装的Promise实例中,只有p1、p2、p3的状态都变为Fullfilled,p的状态才会变为Fullfilled,而p123中只要有一个变为Rejected,p就会变为RejectedPromise.race()
中,只要有一个实例率先改变状态,那么p的状态就会跟着改变 -
注意:如果p包含的Promise实例(如p1)中,有catch函数用于处理其rejected状态,那么该实例即便触发了rejected,在catch过后仍然会返回成resolved状态,这点需要注意
-
示例代码
const databasePromise = connectDatabase(); const booksPromise = databasePromise.then(findAllBooks); const userPromise = databasePromise.then(getCurrentUser); Promise.all([ booksPromise, userPromise ]);
6. resolve方法和reject方法
Promise.resolve()
- resolve方法可以将现有对象转化为Promise对象
- 根据参数不同,resolve的行为也有所不同:
- 当参数是一个Promise实例:resolve方法不会做出任何修改,原封不动地返回该实例
- 当参数是一个thenable对象:
- thenable对象是指具有then方法的对象,then方法可以是API自带也可以是手动定义的
- resolve方法会自动将这个对象转化为Promise对象,然后立刻执行其中的then方法将对象变为Resolved状态
- 当参数不是具有then方法的对象或根本不是对象:
- resolve方法会生成一个新的Promise对象,且该对象状态为Resolved,回调函数会立刻执行
- 无参数:直接返回一个Resolve状态的Promise对象
Promise.reject()
- 和resolve差不多,区别只是把所有的状态都变为reject相关而已