Promise总结

Promise介绍

定义

Promise 是异步编程的一种解决方案:

  • 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;
  • 从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。

语法

ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。

const promise = new Promise((resolve, reject) => {
       // 异步处理
       // 处理结束后、调用resolve 或 reject
});

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

三种状态

  • pending(等待态)
  • fulfiled(成功态)
  • rejected(失败态)

状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

属性

  • Promise.length:length属性,其值总是为 1 (构造器参数的数目).
  • Promise.prototype:表示 Promise 构造器的原型.

特点

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

Promise API概述

1. Promise.prototype.then()

当resolve(成功)/reject(失败)的回调函数

// onFulfilled 是用来接收promise成功的值
// onRejected 是用来接收promise失败的原因
promise.then(onFulfilled, onRejected);

注意:then方法是异步执行的

promise.then方法每次调用都返回一个新的promise对象,所以可以链式写法。

function taskA() {
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejected(error) {
    console.log("Catch Error: A or B", error);
}

var promise = Promise.resolve();
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected) // 捕获前面then方法中的异常

2. Promise.prototype.catch()

在链式写法中可以捕获前面then中发送的异常

promise.catch(onRejected)
// 相当于
promise.then(null, onRrejected);

// 注意:onRejected 不能捕获当前onFulfilled中的异常
promise.then(onFulfilled, onRrejected); 
// 可以写成:
promise.then(onFulfilled)
       .catch(onRrejected); 

3. Promise.prototype.finally()

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

// then返回了一个resolved为2的promise
Promise.resolve(1).then(()=>2,()=>3);
// Promise {<resolved>: 2}

// finally不改变原来resolved的状态
Promise.resolve(1).finally(()=>2);
// Promise

finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。

finally本质上是then方法的特例。

等价的async function写法

// 1. fulfilled情形
Promise.resolve(1).finally(alert);
// Promise {<resolved>: 1}

f = async ()=>{
  try{
    return 1;
  }finally{
    alert();
  }
};
f();
// Promise {<resolved>: 1}

// 2. rejected情形
Promise.reject(1).finally(alert);
// Promise {<rejected>: 1}

f = async ()=>{
  try{
    throw 1;
  }finally{
    alert();
  }
};
f();
// Promise {<rejected>: 1}

4. Promise.resolve()

onFulfilled会被调用,返回一个fulfilled状态的promise对象

const promise = new Promise((resolve, reject) => {
   resolve('fulfilled'); // 状态由 pending => fulfilled
});
promise.then(res => { 
    // onFulfilled 会被调用
    console.log(res); // 'fulfilled' 
}, rej => { 
    // onRejected 不会被调用
})

// promise相当于
Promise.resolve('fulfilled');

5. Promise.reject()

onRejected会被调用,返回一个rejected状态的promise对象

const promise = new Promise((resolve, reject) => {
   reject('rejected'); // 状态由 pending => rejected
});
promise.then(res => { 
    // onFulfilled 不会被调用
}, rej => { 
    // onRejected 会被调用
    console.log(rej); // 'rejected'
})

// promise相当于
Promise.reject('rejected');

6. Promise.all()

接收一个promise对象数组为参数,只有全部为resolve才会调用。通常会用来处理多个并行异步操作。

谁跑的慢,以谁为准执行回调

const p1 = new Promise((resolve, reject) => {
    resolve(1);
});

const p2 = new Promise((resolve, reject) => {
    resolve(2);
});

const p3 = new Promise((resolve, reject) => {
    resolve(3);
});

Promise.all([p1, p2, p3]).then(data => { 
    // 三个都成功则成功 
    console.log(data); // [1, 2, 3] 结果顺序和promise实例数组顺序是一致的
}, err => {
    // 只要有失败,则失败 
    console.log(err);
});

7. Promise.race()

接收一个promise对象数组为参数,Promise.race只要有一个promise对象进入FulFilled或者 Rejected状态的话,就会继续进行后面的处理。

谁跑的快,以谁为准执行回调

function timerPromisefy(delay) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
var startDate = Date.now();

Promise.race([
    timerPromisefy(10),
    timerPromisefy(20),
    timerPromisefy(30)
]).then(function (values) {
    console.log(values); // 10
});

race的使用场景:比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作。

//请求某个图片资源
function requestImg(){
    return new Promise((resolve, reject) => {
        var img = new Image();
        img.onload = function(){
            resolve(img);
        }
        img.src = '图片的路径';
    });
}
//延时函数,用于给请求计时
function timeout(){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('图片请求超时');
        }, 5000);
    });
}
Promise.race([
    requestImg(), 
    timeout()
]).then(res =>{
    console.log(res);
}).catch(err => {
    console.log(err);
});

红绿灯问题

题目:红灯三秒亮一次,绿灯一秒亮一次,黄灯2秒亮一次;如何让三个灯不断交替重复亮灯?(用 Promise 实现)

三个亮灯函数已经存在:

function red(){
    console.log('red');
}
function green(){
    console.log('green');
}
function yellow(){
    console.log('yellow');
}

利用 then 和递归实现:

var light = function(timmer, cb){
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            cb();
            resolve();
        }, timmer);
    });
};

var step = function() {
    Promise.resolve().then(function(){
        return light(3000, red);
    }).then(function(){
        return light(2000, green);
    }).then(function(){
        return light(1000, yellow);
    }).then(function(){
        step();
    });
}

step();

Promise局限性

1. 吞掉错误或异常(顺序错误处理)

throw new Error('error');
console.log(1234567);

在这种情况下,因为 throw error 的缘故,代码被阻断执行,并不会打印 1234567。

const promise = new Promise(null);
console.log(1234567);

以上代码依然会被阻断执行,这是因为如果通过无效的方式使用 Promise,并且出现了一个错误阻碍了正常 Promise 的构造,结果会得到一个立刻跑出的异常,而不是一个被拒绝的 Promise。

let promise = new Promise(() => {
    throw new Error('error')
});
console.log(1235467);

这次会正常的打印 1234567,说明 Promise 内部的错误不会影响到 Promise 外部的代码,而这种情况我们就通常称为 “吞掉错误”。

其实这并不是 Promise 独有的局限性,try..catch 也是这样,同样会捕获一个异常并简单的吃掉错误。

而正是因为错误被吃掉,Promise 链中的错误很容易被忽略掉,这也是为什么会一般推荐在 Promise 链的最后添加一个 catch 函数,因为对于一个没有错误处理函数的 Promise 链,任何错误都会在链中被传播下去,直到你注册了错误处理函数。

2. 单一值

Promise 只能有一个完成值或一个拒绝原因,然而在真实使用的时候,往往需要传递多个值,一般做法都是构造一个对象或数组,然后再传递,then 中获得这个值后,又会进行取值赋值的操作,每次封装和解封都无疑让代码变得笨重。

建议是使用 ES6 的解构赋值:

Promise.all([Promise.resolve(1), Promise.resolve(2)])
.then(([x, y]) => {
    console.log(x, y);
});

3. 无法取消

Promise 一旦新建它就会立即执行,无法中途取消。

4. 无法得知 pending 状态

当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

posted @ 2019-07-18 10:32  璇璇璇璇璇  阅读(257)  评论(0编辑  收藏  举报