Promise

在JavaScript的世界中,所有代码都是单线程执行的。为了使程序不阻塞执行有了异步(I/O操作、事件操作),但是异步也有其不好之处,例如:异步回调callback回调地狱的问题,伴随着这些问题有了解决方案Promise。

快速导航

面试指南

  • Promise 中 .then 的第二参数与 .catch 有什么区别?,参考:错误捕获
  • 怎么让一个函数无论promise对象成功和失败都能被调用?,参考:finally

promise的基本使用和原理

  1. 如何异常捕获(Error、reject)通过catch捕获
  2. 多个串联-链式执行的好处
  3. Promise.all和Promise.race
  4. Promise标准-状态变化(Pending —— Fulfilled/Rejected)
  5. then函数,不明文指定返回实例,返回本身的promise实例,否则返回指定的promise实例

callback方式书写

回调函数方式书写,如果异步请求多了,将会很难维护,程序看着很乱,最终会导致回调地狱。

  1. {
  2. let ajax = function(callback){
  3. console.log('执行');
  4. setTimeout(function(){
  5. callback && callback()
  6. });
  7. }
  8. ajax(function(){
  9. console.log('执行 ajax方法');
  10. })
  11. }

promise方式书写

  • resove:执行下一步操作
  • reject:中断当前操作
  • then:是Promise返回的对象,执行下一个,如果有两个函数,第一个表示resolved(已成功),第二个表示rejected(已失败)
  1. let ajax = function(){
  2. console.log('promise','执行');
  3. return new Promise(function(resolve,reject){
  4. setTimeout(function(){
  5. resolve()
  6. },1000);
  7. });
  8. }
  9. ajax().then(function(){
  10. console.log('promise','执行ajax方法');
  11. });
  • 执行两个Promise的效果
  1. {
  2. let ajax = function(){
  3. console.log('promise','执行');
  4. return new Promise(function(resolve,reject){
  5. setTimeout(function(){
  6. resolve()
  7. },1000);
  8. });
  9. }
  10. ajax()
  11. .then(function(){
  12. return new Promise(function(resolve,reject){
  13. setTimeout(function(){
  14. resolve();
  15. },1000);
  16. });
  17. })
  18. .then(function(){
  19. console.log('promise3','执行3');
  20. })
  21. }
  • 多个Promise实例实现串行操作

执行a b c d 如果中间出了错误使用catch来捕获

  1. {
  2. let ajax = function(num){
  3. console.log('执行4');
  4. return new Promise(function(resolve,reject){
  5. if (num > 5) {
  6. resolve();
  7. }else{
  8. throw new Error('出错了')
  9. }
  10. });
  11. }
  12. ajax(6).then(function(){
  13. console.log('log','6');
  14. }).catch(function(err){
  15. console.log('catch',err);
  16. });
  17. ajax(3).then(function(){
  18. console.log('log','3');
  19. }).catch(function(err){
  20. console.log('catch','err');
  21. });
  22. // 输出:
  23. // 执行4
  24. // 执行4
  25. // log 6
  26. // catch err
  27. }

finally

finally() 方法返回一个Promise,在promise执行结束时,无论结果是fulfilled或者是rejected,在执行then()和catch()后,都会执行finally指定的回调函数。这为指定执行完promise后,无论结果是fulfilled还是rejected都需要执行的代码提供了一种方式,避免同样的语句需要在then()和catch()中各写一次的情况。

  1. Promise.resolve('success').then(result => {
  2. console.log('then: ', result)
  3. return Promise.resolve(result);
  4. }).catch(err => {
  5. console.error('catch: ', err);
  6. return Promise.reject(err);
  7. }).finally(result => {
  8. console.info('finally: ', result);
  9. })
  10. // then: success
  11. // finally: undefined
  12. // Promise {<resolved>: "success"}

promise并行执行

Promise.all()

Promise.all是将多个Promise实例当成一个Promise实例,all方法里是一个数组,数组传进来多个Promise实例,当多个Promise实例状态发生改变的时候,这个新的Promise实例才会发生变化。```javascript//所有图片加载完在添加到页面上function loadImg(src){ return new Promise((resolve,reject) => { let img = document.createElement(‘img’); img.src = src; img.onload = () => { resolve(img); } img.onerror = (err) => { reject(err) } })}

function showImgs(imgs){ imgs.forEach(function(img){ document.body.appendChild(img) })}

// 每个loadImg()方法都是一个Promise实例只有当三个都发生该变化,才会执行新的Promise实例既Promise.all()Promise.all([ loadImg(‘http://www.qzfweb.com/uploads/20170512190539489.jpeg‘), loadImg(‘http://www.qzfweb.com/uploads/20170225143135972.jpg‘), loadImg(‘http://www.qzfweb.com/uploads/20170217225453679.jpg‘)]).then(showImgs)

  1. ## promise率先执行
  2. ## Promise.race()
  3. > **Promise.race**只要其中一个实例率先发生改变,**Promise.race**实例也将发生改变,其他的将不在响应。
  4. ```js
  5. {
  6. // 有一个图片加载完就添加到页面上
  7. function loadImg(src){
  8. return new Promise((resolve,reject) => {
  9. let img = document.createElement('img');
  10. img.src = src;
  11. img.onload = () => {
  12. resolve(img);
  13. }
  14. img.onerror = (err) => {
  15. reject(err)
  16. }
  17. })
  18. }
  19. function showImgs(img){
  20. let p = document.createElement('p');
  21. p.appendChild(img);
  22. document.body.appendChild(p);
  23. }
  24. Promise.race([
  25. loadImg('http://www.qzfweb.com/uploads/20170512190539489.jpeg'),
  26. loadImg('http://www.qzfweb.com/uploads/20170225143135972.jpg'),
  27. loadImg('http://www.qzfweb.com/uploads/20170217225453679.jpg')
  28. ]).then(showImgs)
  29. }

错误捕获

Promise.then第二个参数与catch捕获错误的区别?

  • .then第二参数捕获错误

.then第二个回调参数捕获错误具有就近的原则,不会影响后续then的进行。

  1. {
  2. const ajax = function(){
  3. console.log('promise开始执行');
  4. return new Promise(function(resolve,reject){
  5. setTimeout(function(){
  6. reject(`There's a mistake`);
  7. },1000);
  8. });
  9. }
  10. ajax()
  11. .then(function(){
  12. console.log('then1');
  13. return Promise.resolve();
  14. }, err => {
  15. console.log('then1里面捕获的err: ', err);
  16. })
  17. .then(function(){
  18. console.log('then2');
  19. return Promise.reject(`There's a then mistake`);
  20. })
  21. .catch(err => {
  22. console.log('catch里面捕获的err: ', err);
  23. })
  24. // 输出
  25. // promise开始执行
  26. // then1里面捕获的err: There's a mistake
  27. // then2
  28. // catch里面捕获的err: There's a then mistake
  29. }
  • catch捕获错误

Promise抛错具有冒泡机制,能够不断传递,可以使用catch统一处理,下面代码中不会输出then1 then2会跳过,直接执行catch处理错误

  1. {
  2. const ajax = function(){
  3. console.log('promise开始执行');
  4. return new Promise(function(resolve,reject){
  5. setTimeout(function(){
  6. reject(`There's a mistake`);
  7. },1000);
  8. });
  9. }
  10. ajax()
  11. .then(function(){
  12. console.log('then1');
  13. return Promise.resolve();
  14. })
  15. .then(function(){
  16. console.log('then2');
  17. return Promise.reject(`There's a then mistake`);
  18. })
  19. .catch(err => {
  20. console.log('catch里面捕获的err: ', err);
  21. })
  22. // 输出
  23. // promise开始执行
  24. // catch里面捕获的err: There's a then mistake
  25. }

总结: 不论是Promise还是async/await在写法上解决了异步回调的问题,但是任何写法都不会改变JS单线程、异步的本质,除非js执行引擎发生变化。

资料推荐

Promise/A+规范参考http://www.ituring.com.cn/article/66566