Promise相关

Posted by Eric on November 23, 2018

什么是 Promise

Promise 是异步编程的一种解决方案,比传统的异步解决方案【回调函数】和【事件】更合理、更强大。现已被 ES6 纳入进规范中。

  • Promise 操作只会处在 3 种状态的一种:未完成态(pending)、完成态(resolved) 和失败态(rejected);
  • Promise 的状态只会出现从未完成态向完成态或失败态转化;
  • Promise 的状态一旦转化,将不能被更改;

API

Promise.resolve(value)

类方法,该方法返回一个以 value 值解析后的 Promise 对象

  1. 如果这个值是个 thenable(即带有 then 方法),返回的 Promise 对象会“跟随”这个 thenable 的对象,采用它的最终状态(指 resolved/rejected/pending/settled)
  2. 如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回。
  3. 其他情况以该值为成功状态返回一个 Promise 对象。

上面是 resolve 方法的解释,传入不同类型的 value 值,返回结果也有区别。这个 API 比较重要,建议大家通过练习一些小例子,并且配合上面的解释来熟悉它。

如下几个小例子:

//如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回。  
function fn(resolve){
    setTimeout(function(){
        resolve(123);
    },3000);
}
let p0 = new Promise(fn);
let p1 = Promise.resolve(p0);
// 返回为true,返回的 Promise 即是 入参的 Promise 对象。
console.log(p0 === p1);

传入 thenable 对象,返回 Promise 对象跟随 thenable 对象的最终状态。
ES6 Promises 里提到了 Thenable 这个概念,简单来说它就是一个非常类似 Promise 的东西。最简单的例子就是 jQuery.ajax,它的返回值就是 thenable 对象。但是要谨记,并不是只要实现了 then 方法就一定能作为 Promise 对象来使用。
//如果传入的 value 本身就是 thenable 对象,返回的 promise 对象会跟随 thenable 对象的状态。
let promise = Promise.resolve($.ajax('/test/test.json'));// => promise对象
promise.then(function(value){
   console.log(value);
});

返回一个状态已变成 resolved 的 Promise 对象。
let p1 = Promise.resolve(123); 
//打印p1 可以看到p1是一个状态置为resolved的Promise对象
console.log(p1)

Promise.reject

类方法,且与 resolve 唯一的不同是,返回的 promise 对象的状态为 rejected。

Promise.prototype.then

实例方法,为 Promise 注册回调函数,函数形式:fn(vlaue){},value 是上一个任务的返回结果,then 中的函数一定要 return 一个结果或者一个新的 Promise 对象,才可以让之后的then 回调接收。

Promise.prototype.catch

实例方法,捕获异常,函数形式:fn(err){}, err 是 catch 注册 之前的回调抛出的异常信息。

Promise.race

类方法,多个 Promise 任务同时执行,返回最先执行结束的 Promise 任务的结果,不管这个 Promise 结果是成功还是失败。

Promise.all

类方法,多个 Promise 任务同时执行。如果全部成功执行,则以数组的方式返回所有 Promise 任务的执行结果。 如果有一个 Promise 任务 rejected,则只返回 rejected 任务的结果。

Promise.wrap

回调函数转 Promise

const promiseFoo = Promise.wrap(foo)
 promiseFoo(1, 2) .then((data) => { console.log(data) }) .catch((err) => { console.log(err) })

promise解决的问题

promise解决的问题:回调地狱

回调地狱带来的负面作用有以下几点:

  • 代码臃肿。
  • 可读性差。
  • 耦合度过高,可维护性差。
  • 代码复用性差。
  • 容易滋生 bug。
  • 只能在回调里处理异常。

var p1 = new Promise(function(resolve, reject){
  resolve(Promise.resolve('resolve'));
});

var p2 = new Promise(function(resolve, reject){
  resolve(Promise.reject('reject'));
});

var p3 = new Promise(function(resolve, reject){
  reject(Promise.resolve('resolve'));
});

p1.then(
  function fulfilled(value){
    console.log('fulfilled: ' + value);
  }, 
  function rejected(err){
    console.log('rejected: ' + err);
  }
);

p2.then(
  function fulfilled(value){
    console.log('fulfilled: ' + value);
  }, 
  function rejected(err){
    console.log('rejected: ' + err);
  }
);

p3.then(
  function fulfilled(value){
    console.log('fulfilled: ' + value);
  }, 
  function rejected(err){
    console.log('rejected: ' + err);
  }
);


控制台输出:
p3 rejected: [object Promise]
p1 fulfilled: resolve
p2 rejected: reject

说明

Promise回调函数中的第一个参数resolve,会对Promise执行”拆箱”动作。即当resolve的参数是一个Promise对象时,resolve会”拆箱”获取这个Promise对象的状态和值,但这个过程是异步的。p1”拆箱”后,获取到Promise对象的状态是resolved,因此fulfilled回调被执行;p2”拆箱”后,获取到Promise对象的状态是rejected,因此rejected回调被执行。但Promise回调函数中的第二个参数reject不具备”拆箱“的能力,reject的参数会直接传递给then方法中的rejected回调。因此,即使p3 reject接收了一个resolved状态的Promise,then方法中被调用的依然是rejected,并且参数就是reject接收到的Promise对象。

promise.then(…).catch(…);与promise.then(…, …);并不等价, 尤其注意当promise.then(…).catch(…);中的then会抛异常的情况下。

promise
    .then(...)    //返回一个新的promise,如果then之前的promise是rejected则延续
    .catch(...);    //又返回一个新的promise,如果catch之前的promise是resolved则延续

promise
    //返回一个新的promise
    //如果then之前的promise是resolved,则由第一个参数返回
    //如果then之前的promise是rejected,则由第二个参数返回。
    .then(..., ...);    
例子:
const fn = () => {
    throw 2;
}

//promise.then(...).catch(...);
Promise.resolve(1)         //{ [[PromiseStatus]]:"resolved", [[PromiseValue]]:1 }
    .then(v => {           //1
        fn();              //抛异常了,then返回一个rejected的promise
        return 3;          //后面不执行了
    })                     //{ [[PromiseStatus]]:"rejected", [[PromiseValue]]:2 }
    .catch(v => {          //v是throw的值2
        console.log(v);    //2
        return 4;          //catch返回一个resolved且值为4的promise
    });                    //{ [[PromiseStatus]]:"resolved", [[PromiseValue]]:4 }
//程序最后正常结束

//promise.then(..., ...);
Promise.resolve(1)         //{ [[PromiseStatus]]:"resolved", [[PromiseValue]]:1 }
    .then(
        v => {             //1
            fn();          //抛异常了,then返回一个rejected的promise
            return 3;      //后面不执行了
        },
        v => {             //这里只有then之前是rejected才执行
            console.log(v);//不执行
            return 4;      //不执行
        }
    );                     //{ [[PromiseStatus]]:"rejected", [[PromiseValue]]:2 }
//程序最后抛异常:Uncaught (in promise) 2
var p = new Promise(function(resolve, reject){
  resolve(1);
});
p.then(function(value){               //第一个then
  console.log(value);
  return value*2;
}).then(function(value){              //第二个then
  console.log(value);
}).then(function(value){              //第三个then
  console.log(value);
  return Promise.resolve('resolve'); 
}).then(function(value){              //第四个then
  console.log(value);
  return Promise.reject('reject');
}).then(function(value){              //第五个then
  console.log('resolve: '+ value);
}, function(err){
  console.log('reject: ' + err);
})

答案:
1
2
undefined
"resolve"
"reject: reject"