Promise对象

promise对象用来管理异步操作(未来某个时刻发生的事),由Promise构造函数生成,有一些内置的api,通过这些api能以同步的代码书写方式来表示异步操作,避免不断嵌套的回调函数的书写方式。可以把Promise对象看做一个容器,里面保存着异步操作的结果。
promise对象用三种状态来表示异步操作的状态:pending(进行中),resolved(已完成),rejected(失败了,抛出错误了),状态只能由pending变成resolved或由pending变成rejected,通过promise对象的api可以(提前)指定当变到resolved或rejected状态时的回调函数,而什么情况下是resolved状态,什么情况下是rejected状态,可以由我们自己决定。

主要api:

  1.生成一个primise对象(实例):

var p = new Promise(function(resolve, reject) {
  // 一些异步操作, 得到操作结果result
  var result = ...
  if (result == 'ok') { // 设置这种情况下调用resolved的回调函数
    resolve(result)
  } else {
    reject('not ok')
  }
})

  其中的resolve, reject分别表示resolved和rejected时的回调函数,并且可以传任意参数。如代码所示,当执行resolve(result)时即是把promise对象的状态变成resolved状态,当执行reject('not ok')时即是把promise对象的状态变成rejected状态.

  2.Promise.prototype.then()设置resolved和rejected的回调函数

p.then(function(val) {
  console.log(val)  // 'ok'
}, function (val) {
  console.log(val) // 'not ok'
})    

  then方法接受两个函数参数,第一个参数就是resolved的回调函数,第二个参数是rejected的回调函数(第二个参数是可选的)

一个简单的例子:

var p = new Promise((resolve, reject)=>{
  var result = true
  var v='aaa '
  if(result) {
    resolve(v)
  } else {
    reject(v)
  }
})
p.then( v => console.log(v+'resolve')  // 'aaa resolve'
, v => console.log(v+'reject') )// 'aaa reject'

一个简单的例子:

console.log(0)
var demo = function(){
  return new Promise((resolve, reject) => {
    setTimeout(resolve, 5000, 'aaa ')
  })
}
demo().then( v => console.log(v+'resolve'), v => console.log(v+'reject'))
console.log(1)

// 运行结果
0
1
(5s后) 'aaa resolve'

一个异步加载图片的简单例子:

function loadImgAsync (url) {
  return new Promise( (resolve, reject) => {
    var img = new Image()
    img.onload = ()=> resolve(img)
    img.onerror = () => reject(new Error('not ok'))
    img.src = url
  })
}
var url = 'http://img3.duitang.com/uploads/item/201509/02/20150902131938_yEJVA.jpeg'
loadImgAsync(url).then(() => console.log('ok'), err => console.log(err.message) )

 .then()方法返回的是一个新的Promise实例,所以可以采用链式写法,.then().then(),前一个then回调函数的return返回值会作为参数传到后一个then的回调函数里,只有当前一个Promise对象状态发生变化才会调用下一个then的方法。

一个简单的例子:

p.then( () => {
    console.log('resolve1')  
    return 111
}, () => {
    console.log('reject1')
    throw "empty"
}).then( v =>  console.log(v+' resolve2'), v => console.log(v+' reject2') )

// 若p执行resolved, 则依次 'resolve1', '111 resolve2' 
// 若p执行rejected, 则依次 'reject1', '111 reject2' 
// 若p执行rejected, 同时注释掉throw "empty"或换成 return new Error(),则会依次 'reject1', ' resolve2'!!!

由此可见只有在程序抛出异常或是Promise对象中手动指定调用reject(),只有这两张情况下会执行reject()。

比如: 假设getJson(url)会返回一个Promise对象,用来执行ajax操作,成功时返回结果作为参数传给resolve函数,失败时返回一个Error对象传给reject函数。

getJSON('/post/1.json')
.then(post => getJSON(post.commentUrl))
.then( comment => console.log(comment), err => console.log(err) )

上例就是先取post信息,取到后再去取comment信息。

3.Promise.prototype.catch((err)=>{})

异步操作抛出错误时的回调函数,相当于.then(null, rejectFun),错误具有'冒泡'特性,会一直向后传递,所以:

p
.then()
.then()
.catch((err)=>{
// 处理前面三个promise产生的错误
})

 一般来说,不推荐p.then(okFun, errorFun), 而应该总是使用p.then(okFun).catch(errorFun), 这种写法更符合同步的写法。注意Promise对象抛出的异常不会传递到外层,所有如果没有.catch(), 即使添加了try{p.then()} catch(err) {},p发生错误时也不会被catch到。

4. Promise.all()

接受一个由Promise实例组成的数组作为参数,同样返回一个Promise实例,相当于把多个Promise实例包装成一个新的Promise实例。如 var p = Promise.all([p1,p2,p3]),只有p1, p2, p3都是resolved,p才会是resolved,此时p1, p2, p3的返回值组成一个数组,传给p的回调函数;只要p1, p2, p3有一个rejected,p就是rejected,此时第一个被rejected的实例的返回值传给p的回调函数。

5. Promise.race([p1, p2,p3])

类似于Promise.all(), 区别是只要p1, p2, p3中有一个实例率先改变状态,p的状态就跟着改变。

6. Promise.resolve()

可以把非Promise对象转为Promise对象,如果已经是Promise对象,则原封不动的返回。

var p = Promise.resolve('hello') 即相当于 var p = new Promise( resolve => resolve('hello')),'hello'不是异步操作,状态会立马变成resolved,立即执行回调函数;

var p = Promise.resolve( $.ajax('/a.json') ) 即相当于 var p = new Promise( resolve => {vap rsp = $.ajax('/a.json') ;resolve(rsp); )

也可以 var p = Promise.resolve(); p.then()

7. Promise.reject()

类似Promise.resolve(),var p = Promise.reject('hello') 即相当于 var p = new Promise( (resolve, reject) => reject('hello'))

8. async, await关键字

Promise的写法虽然比起普通回调函数的写法有很多改进,但一眼看上去,代码完全是Promise的api(.then().catch()),操作本身的语义还不是特别明显,而用async函数的写法最符合语义,没有与语义不相干的代码。

async关键字表示该函数内部有异步操作,await 后面是一个Promise对象,执行到await就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句

一个简单的例子:

var p = () => {return new Promise( resolve => setTimeout(resolve, 5000))}
var fun = async () => {
  console.log(1)
  await p().then(()=>console.log(2))
  console.log(3)
}
fun()
console.log(4)

// 运行结果
//1
//4
//(5s后)2
//3

注意:await 只能运行在一个函数里,且只能运行在async函数里,用在普通函数里会报错。因为await表示这里需要等待,await后面的代码无法立即执行,把await放在async函数里面后,执行到await就会跳出这个函数,继续执行函数外面的代码,等await返回异步操作结果后,再继续执行这个async函数内await后面的代码。另外因为await后面的Promise对象的运行结果有可能是rejected,所有最好把await命令放在try...catch代码块中。

一个sleep函数,暂停程序:

const sleep = (ms) => {
  return new Promise(resolve => setTimeout(resolve, ms))
};
const demo1 = async() =>{
  console.log(1)
  await sleep(5000).then(() => console.log(2))
  console.log(3)
}
demo1();
// demo1() 或 demo2均可以
const demo2 = async() =>{
  console.log(1)
  await sleep(5000)
  console.log(2)
  console.log(3)
}
demo2();

// 运行结果
// 1
// (5s后)2
// 3

 

posted @ 2016-11-07 18:01  荔枝龙眼  阅读(281)  评论(0编辑  收藏  举报

这里是页脚Html代码