es之Promise基础

437 阅读5分钟

前言

传统针对异步操作的解决方案是事件和回调函数,造成层层嵌套回调的问题。Promise避免了这样的问题。

Promise对象代表一个异步操作,有3种状态:Pending(进行中)、ReSolved(已成功)、Rejected(已失败)。P=>F,得到Resolved(已成功)状态;P=>R,得到Rejected(已失败)状态。

Promise对象有以下两个特点:

  • 1、对象的状态不受外界影响。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  • 2、一旦状态改变就不会再变,任何时候都可以得到这个结果。

一些缺点:

  • 1、一旦建立它就立即执行,无法中途取消。
  • 2、如果不设置回调函数,Promise内部抛出的错误不会反应到外部。
  • 3、当处于Pending状态时,无法得知目前进展到哪一个阶段。

基本用法

var promise = new Promise((resolve, reject) => {
    //some code
    console.log('promise');
    resolve();
});
console.log('hi');

promise.then(()=>{
    console.log('then success');
}, () => {
    console.log('then error');
});

//promise
//hi
//then success

resolve 函数的作用将“进行中”变成“已成功”状态。reject 函数的作用将“进行中”变成“已失败”状态。then可以接受两个函数。

Promise新建后就会立即执行,即先打印'promise'。

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

p2.then((res)=>{
    console.log('then', res);
}).catch((error) => {
    console.log('catch', error);
});

//catch xxx

reject函数的参数通常是Error对象的实例,表示抛出的异常。resolve函数的参数除了正常值外,还可能是另一个Promise实例

如上例子中,p1 和 p2 都是 Promise 实例,p2 的 resolve 方法将 p1 作为参数,这时 p1 的状态就会传递给 p2,也就是 p1 的状态决定了 p2 的状态。如果 p1 的状态是 Pending,那么 p2 的回调函数就会等待 P1 状态改变。如果 p1 的状态已经是 Resolved 或者 Rejected 状态,那么 p2 的回调函数将会立即执行。

调用resolve和reject不会终结Promise的参数函数的执行

var p1 = new Promise(function (resolve, reject) {
    reject('xxx'); //最好使用return reject('xxx');
    console.log('promise');
});
console.log('out');

p1.then(()=>{
    console.log('then');
}).catch(() => {
    console.log('catch');
});

//promise
//out
//catch

resolve和reject以后,Promise的使命的完成了,后续的操作应该放到then方法中,resolve和reject后面不应该再执行其他语句。

Promise.prototype.then()

作用就是为Promise实例添加状态改变的回调函数。第一参数Resolved状态的回调,第二个参数可选Rejected状态回调,返回的是一个新的Promise实例。

如果then中的回调函数返回:

  • 一个值,那么then返回的Promise将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。

  • 没有返回值,那么then返回的Promise将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined。

  • 抛出一个错误,那么then返回的Promise将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。

  • 一个已经是接受状态的Promise,那么then返回的Promise也会成为接受状态,并且将那个Promise的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。

  • 一个已经是拒绝状态的Promise,那么then返回的Promise也会成为拒绝状态,并且将那个Promise的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。

  • 一个未定状态(pending)的Promise,那么then返回Promise的状态也是未定的,并且它的终态与那个Promise的终态相同;同时,它变为终态时调用的回调函数参数与那个Promise变为终态时的回调函数的参数是相同的。

Promise.prototype.catch()

是.then(null, rejection)的别名,指定发生错误时的回调函数。catch可以捕获Rejected状态抛出的错误,也可以捕获then中抛出的错误。

Promise的错误具有“冒泡”性质,会一直向后传递,直到被捕获。即错误总会被下一个catch语句捕获。如果没有catch,Promise对象抛出错误不会传递到外层代码,但是浏览器此时会打印错误。

var p1 = new Promise(function (resolve, reject) {
    return resolve('xxx'); //
});

p1.then(()=>{
    console.log('then1');
}).catch(() => {
    console.log('catch');
}).then(()=>{
    console.log('then2');
});

//then1
//then2

catch后面还可以跟then,如果没有报错直接跳过catch执行第二个then,如果第二个then有错误抛出,就与前面的catch没有关系了

其他方法

Promise.prototype.all(),最终状态由所有状态决定

Promise.prototype.race(),最终状态由率先改变状态实例决定

Promise.prototype.finally()

Promise.resolve()

Promise.resolve()作用就是将参数转成Promise实例,转换效果如下:

  • 1、参数本身就是Promise实例

不做任何改变,返回这个实例

  • 2、一个thenable对象

将具有then方法的对象转成Promise对象,然后立即执行thenable对象的then方法

var thenable = {
    then(resolve, reject){
        resolve(32);
    }
}
var promise = Promise.resolve(thenable);

promise.then((value) => {
    console.log(value);
});
//32
  • 3、参数不是对象

如果参数不是对象,生成一个新的Promise对象,返回Promise实例的状态就是Resolved状态,所以回调函数会立即执行,参数会同时传给回调函数。

var promise = Promise.resolve(‘hello’);

promise.then((value) => {
    console.log(value);
});
//hello
  • 4、不带任何参数 直接返回状态是Resolved的Promise实例

注意;立即resolve的Promise对象是在本轮“事件循环”结束是,而不是在下一轮“事件循环”开始时。

题目

const p = Promise.resolve();
const ppp = new Promise((resolve, reject) => {
    const pp = new Promise(resolve => {
        resolve('xxx'); //修改此句resolve(p);
    });
    pp.then(() => {
        console.log('pp1');
    }).then(() => {
        console.log('pp2');
    });
    return resolve();
});

ppp.then(()=>{
    console.log('ppp1');
}).then(() => {
    console.log('ppp2');
}).then(() => {
    console.log('ppp3');
}).then(() => {
    console.log('ppp4');
});

p.then(() => {
    console.log('p1');
}).then(() => {
    console.log('p2');
}).then(() => {
    console.log('p3');
});


//pp1
//ppp1
//p1

//pp2
//ppp2
//p2

//ppp3
//pp3

//ppp4

/////////////
//修改后结果
//ppp1
//p1

//ppp2
//p2
//pp1

//ppp3
//p3
//pp2

//ppp4