网上关于Promise的文章非常之多,但我觉得Promise的难点不在语法上,而是在链式调用和错误处理部分。本文将主要就这两部分进行展开说明。
1. Promise的典型应用
Promise的提出,解决了异步函数回调地狱的问题,也更加符合开放封闭原则;
示例一:
原始写法:
1 2 3 4 5 6 7
   | image.onload = function() {       cb_success() };
  function cb_success(){ 	console.log('success'); }
  | 
 
Promise形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   |  function loadImg(src){   let pr = new Promise((resolve,reject)=>{       var img = document.createElement('img');       img.onload = function(){         resolve(img)       }       img.onerror = function(err){         reject('加载失败')       }       img.src = src;     }   )
    return pr; }
  let pr = loadImg('https://www.baidu.com/img/superlogo_c4d7df0a003d3db9b65e9ef0fe6da1ec.png?where=super');
  pr.then((img)=>{   console.log(img.width)   return img   }).then((img)=>{   console.log(img.height) }).catch(err=>{   console.log(err) })
 
  | 
 
示例二:
1 2 3 4 5
   | fs.readFile(fileA, 'utf-8', function (err, data) {   fs.readFile(fileB, 'utf-8', function (err, data) {        }); });
  | 
 
Promise形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | const fs = require('fs');
  new Promise((resolve,reject)=>{     fs.readFile('./index.html',(err,data)=>{         resolve(data)     }) }).then(res=>{     console.log(res)
                     return new Promise((resolve,reject)=>{         fs.readFile('./page.html',(err,data)=>{             resolve(data)         })     }) }).then(res=>{     console.log(res)   })
  | 
2. Promise异常捕获
@1. 不止在reject的时候会进入catch。前面语法有报错,同样会自动进入catch进行捕获。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | const fs = require('fs');
  new Promise((resolve,reject)=>{          f11s.readFile('./index.html',(err,data)=>{          if(err){             reject(err)             return;         }         resolve(data)     }) }).then(res=>{     console.log(res) }).catch(err=>{     console.log(err)   })
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | const fs = require('fs');
  new Promise((resolve,reject)=>{     fs.readFile('./in123dex.html',(err,data)=>{                   if(err){                          throw new Error(err);             return;         }         resolve(data)     }) }).then(res=>{     console.log(res) }).catch(err=>{     console.log(err)   })
  | 
 
@2 then里面写两个回调的写法在链式调用时,catch捕获异常容易出问题;所以一般在链式调用时推荐在最后写一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   |  let pr = new Promise(function(resolve,reject){   reject(1) })
  pr.then(   ()=>console.log('success 1'),   ()=>console.log('error 1') ) pr.then(   ()=>console.log('success 2'),   ()=>console.log('error 2') )
 
 
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   |  let pr = new Promise(function(resolve,reject){   reject(1) })
  pr.then(   ()=>console.log('success 1'),   ()=>console.log('error 1') ).then(   ()=>console.log('success 2'),   ()=>console.log('error 2') )
 
 
 
  | 
 
所以推荐写法:统一进行错误捕捉
1 2 3 4 5 6 7 8 9 10 11 12 13
   | 
  let pr1 = new Promise(function(resolve,reject){   reject(1) })
  pr1.then(   ()=>console.log('success 1') ).then(   ()=>console.log('success 2') ).catch(err=>{   console.log(err); })
 
  | 
 
3. Promise链式调用
then函数必须返回Promise的实例。
首先,下面这段代码是可以执行的,因为then方法默认返回一个Promise,Promise就有then方法。想要传递参数需要return;
1 2 3 4 5 6 7 8
   | new Promise((resolve, reject) => {     resolve('li') }).then((value) => {   console.log(value)     return 'hello ' + value; }).then(val=>{ 	console.log(val)   })
  | 
 
但Promise规范规定 then回调函数return的应该是一个Promise,所以可以这么写
1 2 3 4 5 6 7 8 9 10
   | new Promise((resolve, reject) => {     resolve('li') }).then((value) => {   console.log(value)     return  new Promise(resolve=>{     resolve('hello ' + value);   }) }).then(val=>{ 	console.log(val)   })
  | 
 
正常使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | const fs = require('fs');
  new Promise((resolve,reject)=>{     fs.readFile('./index.html',(err,data)=>{         resolve(data)     }) }).then(res=>{     console.log(res)
                     return new Promise((resolve,reject)=>{         fs.readFile('./page.html',(err,data)=>{             resolve(data)         })     }) }).then(res=>{     console.log(res)   })
  | 
 
4. Promise.all**
Promise.all的返回值默认是一个数组。在参数里的全部promise运行完成后执行。
如下是工作中的一个需求,需要先从前面两个接口中获取通行证id,再根据这两个id从第三个接口中取聊天记录;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
   | const p1 = getData({         url: 'url1',         method: 'get',         params: {             passportid: info.agentPassportID         } }).then(res => res) .catch(e => alert('获取agentID失败'));
  const p2 = getData({     url: 'url2',     method: 'get',     params: {         passportid: info.customerPassportID     } }).then(res => res) .catch(e => alert('获取customerID失败'));
  Promise.all([p1, p2]) .then(result => {      return getData({         url: 'url3',         method: 'get',         params: {             command: 'getMessage',             from: result[0].data.Data,             sendto: result[1].data.Data         }     }) }) .catch(e => alert('获取聊天记录失败'));
  | 
 
5. Promise.race**
与Promise.all相同,区别是只要有一个完成就算完成。
常见用法:
- 把异步操作和定时器放在一起
 
- 如果定时器先触发,就认定超时,告知用户
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | let p1 = new Promise((resolve,reject)=>{   console.log('this is p1')   setTimeout(function(){     resolve('aaa')   },5000) }) let p2 = new Promise((resolve,reject)=>{   console.log('this is p2')   setTimeout(function(){     resolve('bbb')   },2000) })
  Promise.race([p1,p2]).then(val=>{   console.log(val)   })
  | 
 
6. 在IE中使用Promise**
IE8,9,10,11均不原生支持Promise;IE Edge支持
解决方案: BlueBird/Promise Polyfill