Promise对象


I promise to solve this asynchronous problem.
曾经我们怎么处理异步问题?
function func1() { ... }
function func2() { ... }
function func3() { ... }

func1(() => {
    func2(() => {
       func3(() => {
           ...
       }
    }
)
层次浅的时候看着还没什么,当有一连串的逻辑需要异步处理的时候,就形成回调地狱了,一层套一层,一层套一层,代码也极不工整。promise就是用来解决这一问题。
Promise是异步编程的一种解决方案,写入在ES6的标准当中。Promise其实是一个构造函数,自己身上有all,reject,resolve这几个方法,原型上还有then,catch等方法。
Promise对象的特点:
  1. 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态,pending(进行中),fulfilled(已成功),rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending到fulfilled和从pending到rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

基本用法

Promise构造函数接受一个函数作为参数,这个函数会有两个参数,一个是resolved,代表执行成功,将状态pending转换为fulfilled;一个是reject,代表执行失败,将状态pending转换为rejected
const promiseClick =()=>{
    console.log('点击方法被调用')
    let promise = new Promise(function(resolve, reject){
    //做一些异步操作
    setTimeout(function(){
            console.log('执行完成Promise');
            resolve('要返回的数据可以任何数据例如接口返回数据');
        }, 2000);
    });
    return promise;
}
promiseClick ()
.then(function(data){
    console.log(data);
    return asyncFunc2();
})
.then(function(data){
    console.log(data);
    return asyncFunc3();
})
.then(function(data){
    console.log(data);
});

Promise.prototype.then()

then方法是在Promise的原型上定义的,为Promise实例状态改变时提供回调函数。它返回的是一个新的promise实例,可以继续调用其then方法,因此就可以形成链式结构写法。
new Promise((resolved, rejected) => {
    resolved();
}).then(() => {
    ...
});

Promise.prototype.catch()

catch方法用于指定发生错误时的回调函数,当执行中遇到错误,会被catch中的回调函数捕获到,这里说明一下,reject()的作用相当于抛出错误。
new Promise((resolved, rejected) => {
    throw new Error('err');
}).catch((error) => {
    ...
});
如果抛出错误是在resolved之后执行的,就不会被捕获,因为Promise的状态一旦改变了就不会再二次改变。
Promise对象的错误具有“冒泡”的性质,错误会往后传递到被捕获为止。但是Promise的错误捕获和传统的try/catch有区别,Promise对象抛出的错误正常情况下不会传递到外层,除非有catch()方法指定捕获错误后的回调函数。

Promise.prototype.finally()

这个方法没有其他的方法那么常用,它是后加入的,在ES2018才引入。
在promise结束时,不管状态是fulfilled还是rejected,都会执行finally()指定的回调函数。例如在原本的语法中,有一段逻辑不管promise执行结果如何都会执行,就需要在then()和catch()中分别写一遍,现在就可以直接写在finally()里。
new Promise((resolved, rejected) => {
    resolved();
}).then(() => {
    ...
}).catch((error) => {
    ...
}).finally(() => {
    ...
});
finally()的回调函数没有参数,因为它不需要关注promise的状态。

Promise.all()

这个方法提供了并行执行异步操作的能力,在所有的异步操作都执行完毕后,且所有的执行结果都是成功的时候,才会执行回调函数。
Promise.all()方法的参数经常传的就是一个数组,但其实不仅是数组,只有要Iterator接口的,且返回的是都是Promise实例也可以。
Promise.all()方法像一个合运算,其返回结果取决于所有异步操作的执行结果,如果所有操作都执行成功,状态为fulfilled,则返回为fulfilled,所有异步操作的返回数据会放进一个数组中传给then;只要其中一个异步操作失败,状态为rejected,则返回为rejected,且执行结果与第一个状态为rejected的返回一样。
Promise
.all([promise1(), promise2(), promis3()])
.then(function(results){
    console.log(results);
});

Promise.race()

race()的用法和all()类似,但是作用相反,所有异步操作中哪个先执行完就执行回调函数,不管是执行成功,还是执行失败,其余异步操作会继续执行,但是都不会再进入race()的回调函数。
Promise
.all([promise1(), promise2(), promis3()])
.race(function(results){
      console.log('成功',results);
},function(reason){
      console.log('失败',reason);
});
race()的一个常用场景,在5s内请求成功就走then()方法,5s内没有请求成功就执行另一个操作。
function requestTableList(){
    var p = new Promise((resolve, reject) => {
           //去后台请求数据,这里可以是ajax,可以是axios,可以是fetch 
            resolve(res);
    });
    return p;
}
//延时函数,用于给请求计时 10s
function timeout(){
    var p = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('请求超时');
        }, 10000);
    });
    return p;
}
Promise.race([requestTableList(), timeout()]).then((data) => {
    //进行成功回调处理
    console.log(data);
}).catch((err) => {
    // 失败回调处理
    console.log(err);
});

应用

举一个例子,前端并发控制请求:
var urls = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
const limit = 5;
function sendRequest(urls, limit , callback) {
    function _send (urls) {
        const url = urls.shift();
        if (url) {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    console.log(`当前发送:${url}`);
                    resolve(url);
               }, 10)
            })
            .finally(() => {
                if(urls.length > 0) {
                    return _send(urls)
                }
            })
        }
    }
    let asyncList = [];
    while (limit--) {
        asyncList.push(_send(urls));
    }
    return Promise.all(asyncList).then(callback);
}

sendRequest(urls, limit, function() {
    console.log('all urls sended!')
});
posted @ 2021-09-11 20:07  我是polo  阅读(101)  评论(0)    收藏  举报