Promise定义

在promise以前,js用回调函数来处理异步请求,当上一次请求和下一次请求存在依赖关系的时候,就会出现回调地狱,类似这样嵌套回调

$.post('/getUser', function(data) {
  $.post('/getInfo', userId: data.id, function(){
  ...
  })
})

为了更好的异步流程控制,es6出现了promise,promsie对象,也是可以用来实现异步的,简单的语法如下

new Promise(
  /* executor */
  function(resolve, reject){...}
);

Promise状态

promise有以下三种状态,只要有一种状态被填充,一个promise就执行完毕,可以通过then或者catch来捕捉填充的状态,状态不可逆

  • pending: 初始状态, 初始状态,未完成或拒绝。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。

Promise方法

Promise.all(iterable)

这个方法返回一个新的promise对象,该promise对象在iterable里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。Promise.all方法常被用于处理多个promise对象的状态集合。

const pro1 = new Promise((resolve, reject) => {
  resolve('pro1结果')
});

const pro2 = new Promise((resolve, reject) => {
  resolve('pro2结果');
})

Promise.all([pro1, pro2]).then((res1) => {
  console.log(res1); // ['pro1结果', 'pro2结果']
})

Promise.race(iterable)

当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。

const pro1 = new Promise((resolve, reject) => {
  setTimeout(resolve(100), 100)
});

const pro2 = new Promise((resolve, reject) => {
  setTimeout(resolve(200), 500);
})

Promise.race([pro1, pro2]).then((res) => {
  console.log(res);   // pro1先完成,结果是100
})

Promise.reject(reason)

调用Promise的rejected句柄,并返回这个Promise对象。

Promise.resolve(value)

用成功值value完成一个Promise对象。如果该value为可继续的(thenable,即带有then方法),返回的Promise对象会“跟随”这个value,采用这个value的最终状态;否则的话返回值会用这个value满足(fullfil)返回的Promise对象。

Promise错误捕捉

当有多个promise链式调用的时候,其中一个填充了reject状态,后续的将不会继续执行,例如

function dealOne () {
  return new Promise((resolve, reject) => {
    setTimeout(resolve(200), 500);
  });
}

function dealTwo (a) {
  return new Promise((resolve, reject) => {
    reject('error')
  })
}

function dealThree () {
  return new Promise((resolve, reject) => {
    resolve('3')
  })
}

dealOne().then(dealTwo).then(dealThree).then(res => {
  console.log(res);
}).catch(err => {
  console.log(err);
})

在dealTwo函数中,操作失败了,catch就会捕捉到错误,dealThree不会继续执行

当promise发生错误(reject)时,有两种方法,

1. 通过链式调用catch可以捕捉reject的结果

2. 通过then的第二个回调函数捕捉

dealTwo().then((res) => {}, (err) => { console.log('then ' + err) }).catch( err => console.log('catch' + err) )

在这里由于then的第二个回调捕捉了错误,所以先输出的结果为‘then error’,后面的catch就不执行了

Promise问题

function doSomething() {
  return new Promise((resolve, reject) => {
    resolve('doSomething');
  })
}

function doSomethingElse() {
  return new Promise((resolve, reject) => {
    resolve('doSomethingElse');
  })
}

doSomething().then(function () {
  return doSomethingElse();
}).then(res => {
  console.log(res);
});

doSomething().then(function (res) {
  console.log('receive: ' + res)
  doSomethingElse();
}).then(res => {
  console.log(res);  // undefined
});

doSomething().then(doSomethingElse()).then(res => {
  console.log(res);
});

doSomething().then(doSomethingElse).then(res => {
  console.log(res);
});

这四个东西有什么区别呢?

第一个很好理解:结果是doSomethingElse

第二个,由于第一个then有一个回调函数接收了结果,但是没返回新的promise,所以结果是undefined,其实相当于

doSomething().then(function (res) {
  console.log('receive: ' + res)
  doSomethingElse();
}).then(res => {
  console.log(res);   // undefined
});

第三个,由于then后执行了一个普通函数,所以doSomething()的值会穿透到下一个then接收,类似下面的例子

doSomething().then(null).then().then(res => {
  console.log(res);  // doSomething
})

第四个,其实跟第一个一样,只是简单的写法,所以结果是doSomethingElse

参考资料: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html