Promise
概念
- Promise简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise是一个对象,从它可以获取异步操作的消息
特点
- Promise对象的状态不受外界影响.Promise对象代表一个异步操作,有三种状态:pending(进行中),fulfilled(已成功),rejected(已失败).只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果.Promise对象的状态改变只有两种可能:从pending变为fulfilled和从pending变为rejected.只要这两种情况发生,状态就不会再变了,会一直保持这个结果,这时就称为resolved(已定型).如果改变已经发生了,再对Promise对象添加回调函数,也会立即得到这个结果.这与事件不同,事件一旦错过了再去监听是不能得到结果的
存在的缺点
- 无法取消,一旦创建就会立即执行,无法中途取消
- 如果不设置回调函数,Promise内部抛出的错误不会反应到外部
- 当处于pending状态时,无法得知目前处于哪一个阶段
基本用法
-
ES6规定,Promise对象是一个构造函数,用来生成Promise实例
var promise=new Promise(function(resolve,reject){ if(/*异步操作成功*/){ resolve(value); //resolve将Promise对象从pending状态变为resolve,在异步成功时调用,将异步操作的结果作为参数传递出去 }else{ reject(error); //reject将Promise对象从pending状态变为rejected,在异步失败时调用,将异步操作的错误作为参数传递出去 } })
-
当Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
promise.then(function(value){ //success code ... },function(err){ //第二个函数参数可选 //error code ... })
-
调用resolve或reject并不会导致Promise的参数函数的执行,也就是说即使是已经通过resolve()返回了结果,依然可以执行下面的代码
-
Promise实例具有then方法,定义在Promise.prototype上.作用就是为Promise实例添加状态改变时的回调函数,返回的是一个新Promise实例(不是原来的Promise实例).因此可以采用链式写法
-
Promise.prototype.catch()方法是.then(null,rejection)的别名,用于指定发生错误时的回调函数.此外,then方法指定的回调函数如果运行中抛出错误,也会被catch方法捕获.
-
如果Promise状态已经变为resolved,再抛出错误是无效的
-
Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止.也就是说错误总会被下一个catch语句捕获
getJSON('/post/1.json').then(function(post) { return getJSON(post.commentURL); }).then(function(comments) { // some code }).catch(function(error) { // 处理前面三个Promise产生的错误,分别是getJSON和两个then });
-
一般来说,不要使用then里使用第二个reject状态的回调函数,使用catch最好
-
注意:catch方法返回的还是一个Promise对象,因此后面还可以接着调用then方法.如果没有报错,会跳过catch方法
-
-
Promise.all方法用于将多个Promise实例包装成一个新的Promise实例:
var p=Promise.all([p1,p2,p3])
,如果数组中的元素不是Promise实例,会通过Promise.resolve方法将其转为Promise实例,再进一步处理(Promise.all方法的参数可以不是数组,但必须有iterator接口,且返回的每个成员都是Promise实例).p的状态有两种- p1,p2,p3的状态都变为fulfilled,p的状态才会变为fulfilled,此时p1,p2,p3的返回值组成一个数组,传递给p的回调函数
- 只要p1,p2,p3中有一个被rejected,p的状态就变为rejected,此时第一个被reject的实例的返回值会传递给p的回调函数
-
Promise.race方法同样是将多个Promise实例包装成一个新的Promise实例:
var p=Promise.race([p1,p2,p3])
.只要p1,p2,p3之中有一个实例率先改变状态,p的状态就会跟着改变,那个率先改变的Promise实例的返回值就传递给p的回调函数.如果数组中的元素不是Promise实例,会通过Promise.resolve方法将其转为Promise实例,再进一步处理 -
Promise.resolve:将现有对象转为Promise对象.参数有四种情况
-
参数是一个Promise实例,不作任何修改,原封不动的返回这个实例
-
参数是一个thenable对象(具有then方法的对象),将这个对象转为Promise对象,然后立即执行thenable对象的then方法
let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 });
-
参数不是具有then方法的对象,或根本不是对象.该方法会返回一个新的Promise对象,状态为resolved
-
不带有任何参数,直接返回一个resolved状态的Promise对象.注意:立即resolve的Promise对象,是在本轮"事件循环"的结束时,而不是在下一轮"事件循环"的开始时
setTimeout(function(){ //在下一轮事件循环开始时执行 console.log('three'); },0) Promise.resolve().then(function(){ //在本轮事件循环结束时执行 console.log('two'); }) console.log('one'); //立即执行 //打印顺序为:one two three
-
-
Promise.reject(reason)方法会返回一个新的Promise实例,该实例的状态为rejected,回调函数会立即执行.注意:Promise.reject()方法的参数,会原封不动的作为reject的理由,变成后续方法的参数
const thenable = { then(resolve, reject) { reject('出错了'); } }; Promise.reject(thenable) .catch(e => { console.log(e === thenable) //Promise.reject(thenable)的参数thenable整体作为catch的参数e,也就是原封不动作为reject的理由 }) // true
-
done():Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误可能无法捕捉到(因为Promise内部的错误不会冒泡到全局),因此可以提供一个done方法总是处于会掉链的尾端,保证抛出任何可能出现的错误
asyncFunc() .then(f1) .catch(r1) .then(f2) .done(); //下面实现done方法 Promise.prototype.done=function(onFulfilled, onRejected){ this.then(onFulfilled, onRejected) //可以像then方法一样,提供onFulfilled, onRejected状态的回调函数 .catch(function(reason){ setTimeout(() => {throw reason},0) }) }
-
finally():用于指定不管Promise对象最后状态如何都会执行的操作.它与done方法的最大区别是,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行
Promise.try
-
实际开发中会遇到一种情况,不知道或者不想区分函数f是同步函数还是异步函数,但都想用Promise来处理.因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误.一般采用如下写法:
Promise.resolve().then(f)
.但是如果这样写的话,它会在本轮事件循环结束的末尾执行const f = () => console.log('now'); Promise.resolve().then(f); console.log('next'); // next // now //也就是说f是同步的,但用Promise包装了以后,就变成异步执行了
-
接下来是两种让同步函数同步执行,异步函数异步执行,并具有统一API的写法
-
使用async函数
const f = () => console.log('now'); (async () => f())(); //立即执行的匿名函数,会立即执行里面的async函数,如果f是异步的,可以用then指定下一步. //但是async() = > f() 会吃掉f()抛出的错误,因此要捕获错误要用promise.catch方法 console.log('next'); // now // next //同时处理异步函数的写法 (async() => f())() .then(...) .catch(...)
-
使用new Promise()
const f = () => console.log('now'); ( () => new Promise( resolve => resolve(f()) ) )(); console.log('next'); // now // next 第二行开始的一大段代码,可以用Promise.try(f)来替代
-