Promise对象用于一个异步操作的成功(或失败)及其结果值的表示。(简单点说,就是我们处理异步请求,经常会进行一些承诺,如果我赢了你嫁给我,我输了我嫁给你,这类的诺言,这就是Promise的中文意思,一个诺言,一个成功,一个失败)
ES6将其写进了语言标准,统一了用法,并且原生提供了Promise对象。
我们可以先看一下Promise到底是什么?chrome上输入console.dir(Promise),就出来了。
我们可以看到,原来Promise是个构造函数,它有all,race,reject,resolve方法,在其原型prototype上有catch,then方法。
有的初级的小伙伴就问了,呀呀呀,这个Promise怎么还有方法呢?Promise也是函数哦,函数是对象,当然可以有方法啦,0--^^--0。毫无疑问,原型prototype上的方法就是供Promise的实例对象使用的。
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。resolve函数的作用是,将Promise对象的状态从“”未完成”变为“成功”,在异步操作成功时调用,并将异步操作的结果作为参数传递出去;reject函数的作用是,将Promise对象的状态从“”未完成”变为“失败”,在异步操作失败时调用,并将异步操作的错误作为参数传递出去;
既然这样,我们先实例化一个Promise。
var promise = new Promise(function(resolve, reject) { setTimeout(resolve('some thing happed'), 200) }) promise.then(function(data){ console.log('success:', data) }, function(err){ console.log('error:', err) })
首先得说明,Promise.prototype.then,可以接受两个回调函数,当成功resolve时调用第一个回调,失败时调用第二个回调,第二个回调可以省略。
所以上面的结果为 success: some thing happed
初步了解Promise后,我们用Promise对象实现一个AJAX操作的例子。
var getJSON = function(url) { var promise = new Promise(resolve, reject) { var client = new XMLHttpRequest(); client.open('GET', url); client.onreadystatechange = function(){ if (this.readyState !== 4) { return } if (this.status === 200) { resolve(this.response) } else { reject(new Error(this.statusText)) } } } return promise } getJSON('/db.json').then(function(json){ console.log('Result:', json) }, function(error){ console.log('error:', error) })
上面的代码中,getJSON是对XMLHttpRequest对象的Promise封装,用于发出一个针对JSON数据的HTTP请求,并返回一个Promise对象。需要注意的是,在getJSON内部,resolve函数和reject函数调用时都带有参数。reject函数的参数通常是Error对象的实例,而resolve函数的参数除了正常的值外,还可以是另一个Promise对象。
举例说明
var p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) var p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2.then(result => console.log('success:', result)) p2.catch(error => console.log('error:', error))
在这种情况下,p1的状态会传递给p2,p1的状态决定了p2的状态,一秒钟后,p1的状态还是pending,所以p2也是pending,三秒后,p1的状态变为failed,p2的状态也变为failed,所以调用catch方法,返回结果为error: Error: fail,利用这个特性,我们可以用来控制多个异步操作的流程???
需要注意的是Promise.prototype.then方法和Promise.prototype.catch方法返回的都是Promise对象,所以可以继续调用then方法。
前面有看到,Promise构造函数有all和race方法,让我们逐一击破
Promise.all方法用于将多个Promise实例包装成一个新的Promise实例
var p = Promise.all([p1, p2, p3])
此时p的状态有p1,p2,p3的状态决定,规则有两条,
1、只有p1,p2,p3的状态都变成Fullfilled时,p的状态才会变成Fullfilled,此时p1,p2,p3的返回值组成一个数组,传递给p的回调函数
2、只要p1,p2,p3中一个被Rejected,p的状态就变成Rejected,此时第一个被Rejected的实例的返回值传递给p的回调函数
这就是all的意义啦。比如
var promises = [2,3,4].map(function(id){ return getJSON('/post/' + id + '.json'); }) Promsie.all(promises).then(function(posts){ //3个各自成功返回值组成的数组 }).catch(function(reason){ //第一个失败返回值 })
Promise.race()同样是将多个Promise实例包装成一个新的Promise实例
var p = Promise.race([p1,p2,p3])
只要p1,p2,p3中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的回调函数。就不在赘述。
前面也有看到,Promise构造函数还有resolve和reject方法,它们各自又有什么用呢?
有时,我们需要将现有对象转化为Promise对象,Promise.resolve方法就起到这个作用。如
var jsPromise = Promise.resolve($.ajax('/whatever.json')); jsPromise.then(function(data){ //成功 console.log('success:', data) },function(err){ //失败 console.log('error:', err) })
上面的代码可以将jquery生成的deferred对象转化新的Promise对象,jsPromise调用哪个方法还是取决于jquery中deferred对象的状态。
Promise.resolve('foo') 等价于 new Promise(resolve => resolve('foo'))
如果Promise.resolve方法的参数不是具有then()方法的对象,则返回一个新的Promise对象,且其状态为Resolved。
var p = Promise.resolve('Hello') p.then(function(s){ console.log(s) }) //hello
由于字符串'hello'不属于异步操作(没有then方法),返回Promise实例的状态从一生成就是Resolved,所以回调函数会立即执行。
Promise.reject(reason)则是返回一个新的Promise实例,状态为Rejected。Promise.reject方法的参数reason会被传递给实例的回调函数
var p = Promise.reject('出错了') //等同于 var p = new Promise((resolve,reject) => reject('foo')) p.then(null, function (s) { console.log(s) }) //出错了