• Promise是什么?


    一. Promise是什么?

    Promise是一种异步操作的解决方案,将写法复杂的传统的回调函数和监听事件的异步操作,用同步代码的形式表达出来。

    避免了多级异步操作的回调函数嵌套。

    Promise最早是一个函数库,现在已经被ES6写入标准用法。

    Promise是一种构造函数。

    Promise实例创建后会立即执行。

    new Promise((resolve,reject)=> { // resolve,reject是形式参数,可以是任意写法,如(res, rej),默认第一个参数实现的是resolve功能;第二个参数实现的是reject功能。
        console.log("1");
        resolve("success")
    });
    console.log("2"); 
    // 执行结果
    1
    2

    创建实例如下:

     1 //构造函数传参格式固定,就是函数加(resolve,reject)的参数,这是规定!!
     2 const myPromise = new Promise(function(resolve, reject){ 
     3    //自己需要实现的异步操作的内容
     4     ......
     5     ......
     6    if(成功) resolve(value); 
     7     // resolve是js引擎提供定的成功后调用的函数,作用就是手动将该promise的状态由 
     8     // pending(未完成)变为fulfilled(异步操作成功),然后立即触发then中的成功回调函数
     9     // value值是异步操作的结果,要传给后面的回调函数的值
    10     // 值的内容可以根据业务需求自己定;可以是值(普通对象,函数,字符,数字等)也可以是Promise对象
    11    else reject(new Error);
    12     // reject也是js引擎提供的失败后调用的函数,作用就是手动将状态由pending变为 
    13     // rejected(异步操作失败),参数是抛出的异常。然后才能触发then中的错误回调函数或者catch中的回调函数
    14 })

     Promise的参数函数

    new Promise(function(resolve, reject){
       //....
    })

    参数函数的resolve方法除了可以返回数据处理结果,还可以返回一个promise实例,

    当返回promise实例的时候(resolve(promiseObj)),当前实例的状态无效,状态以参数实例(promiseObj)的状态为准。

    返回的结果也以参数实例的返回结果为准。

    示例:

    // 将实例p1作为p2的resolve返回参数
    const p1 = new Promise(function(resolve, reject){
        setTimeout(() => resolve("success"), 2000);
    });
    const p2 = new Promise(function(resolve, reject){
        setTimeout(() => resolve(p1), 1000);  // p2的状态由p1决定
    });
    p2.then(function(data) {
        console.log(data); // success 2秒后打印p1的返回结果;说明then方法在状态改变后被触发。
    }, function() {
    }).catch(err => console.log(err))

    resolve()不同于return, resolve()执行完成后后面的代码还会执行。

    new Promise((resolve,reject)=> {
        resolve("success");
        console.log("1"); //会打印
    });
    // 一般状态改变后的操作放在then回调函数中
    // 所以最好写成
    new Promise((resolve,reject)=> {
        return resolve("success");
        console.log("1"); //不再执行,最好不要在状态改变后添加操作
    });

    二、Promise的实例方法

    1. Promise.prototype.then()

    then方法是Promise构造函数原型对象上的方法。

    作用: 为实例状态改变时添加回调函数(相当于实例状态变化的监听函数)。

    1)then方法接受两个回调函数作为参数,第一个函数是实例状态变为resolved(fulfilled)时的回调函数;

    第二个函数是实例状态变为rejected时的回调函数。

    myPromise.then(function(value){
      // 前面实例中resolve方法执行后触发该方法,即状态为fulfilled(resolved)状态时触发的回调函数
       // value值就是resolve的参数值
    },function(err) {
       // 前面实例中reject方法执行后会触发该方法,即状态为rejected时
    })

    2)then方法返回一个新的Promise实例,所以可以采用链式写法;

    then方法的第一个回调函数的返回值,即return的值作为下一个then方法里面的函数传参。

    如果返回值是非Promise对象,会触发下一个then方法的第一个回调函数, 并且返回值作为回调函数的参数;

    const p1 = new Promise(function(resolve, reject) {
        resolve("success")
    })
    function fn() { return 5 } // 不论返回值是什么,返回结果结果都是下个then方法的第一个函数的参数,即使return new Error()
    p1.then(fn).then(
        result => console.log("1-->",result), //1-->5 如果fn返回new Error('fail'),则1-->Error: fail
        err => console.log('err-->',err) // 不执行
    )

    如果前一个then方法返回值是promise对象,则程序会等待该promise对象的状态发生变化(resolved或rejected)时触发下一个then方法。

    const p1 = new Promise(function(resolve, reject) {
        resolve("success")
    })
    const p2 = new Promise(function(resolve, reject) {
        reject(new Error("p2"))
    })
    function fn() {
        return p2; // 返回Promise对象;
    }
    p1.then(fn).then(
        result => console.log('1-->',result), // 不执行
        err => console.log('err-->',err) // err-->Error: p2 
    )

    3)then方法rejected状态具有传递性,即如果实例调用reject(),则在所有的then方法的第二个函数中都能取到reject返回的值。

    如果确实发生错误,则会执行最近的then方法的第二个回调函数,之后的错误回调函数都不再执行

    new Promise(fn)
    .then(f1) // 省略了rejected状态的回调函数,但是实际运行的时候还是会默认调用该then方法
    .then(f2)
    .then(f3)
    .then(f4, r1) // 这种写法不好,最好在最后使用catch方法
    .then(f4, r2)
    // 如果promise实例状态为rejected, 那么f1,f2,f3,f4都不会执行,但是r1执行,因为报错具有传递性。但是fulfilled状态的话,只能触发最近的成功回调
    // r2不执行,错误相当于被r2捕获

    4) 如确认then方法中回调函数的返回值问题

    const p1 = new Promise(function(resolve, reject) {
        resolve("success")
    })
    function fn() {
        return 5;
    }
    p1.then(fn()).then(result => console.log('1',result));// "success"  .then(fn)相当于then(5),忽略该then方法
    p1.then(fn).then(result => console.log('2',result)); // 5
    p1.then(function(){fn()}).then(result => console.log('3',result)); // undefined
    p1.then(function(){return fn()}).then(result => console.log('4',result)); // 5

    5) then方法中的回调函数是异步任务,是微任务;诸如setTimeout是宏任务;

    微任务加入本轮事件循环最下面;宏任务加入下一轮事件循环。因为主栈的任务也属于宏任务。

    setTimeout(() => console.log('1'),0);
    new Promise((resolve,reject)=> resolve(1)).then(() => console.log('2'))
    console.log('3');
    // 执行结果如下:
    3
    2
    1

    2. Promise.prototype.catch()

    Promise原型上的catch方法,相当于.then(null/undefined, () => {}),所以返回的也是promise,后面还可以跟then或者catch,只在rejected状态触发。

    1. 它可以捕获所有之前执行的promise中的错误

    最好所有的promise都在最后使用catch方法,不要在then方法中写第二个回调函数

    myPromise.then((val) => {
      throw new Error("failed");
    }).then((val) => {
        // TODO
    }).catch((err) => {
    console.log(err); // Error: failed 捕获内部异常 })

    2. 在promise实例中,throw异常发生在resolve()之后, 抛出异常被忽略。

    因为resolve之后,状态改变后永久保留该状态,不能再次改变。throw本质上是将状态修改为rejected。

    new Promise((resolve,reject)=> {
        resolve('ok');
        throw new Error('ok');
    }).then((data) => console.log('data-->',data)) // data-->ok
    .catch(err => console.log('err-->',err)); // 不执行;抛出异常被忽略
    
    // 执行结果如下:
    data-->ok
    
    // throw new Error('ok') 相当于
    new Promise((resolve,reject)=> {
        resolve('ok'); // 状态已经改变;后面本质也是修改状态;Promise状态改变后不再变化。
        try {
            throw new Error('ok');
       }catch (err) {  //也说明了promise内部异常不会影响外部
            reject(err);
       }
    }).then((data) => console.log('data-->',data)) // data-->ok
    .catch(err => console.log('err-->',err)); // 不执行;抛出异常被忽略    

    如果抛出异常的位置在resolve方法后且在一个异步任务中,如setTimeout中,则在"下一轮"事件循环触发,此时promise已经执行完成。

    因为then方法是微任务,比setTimeout先执行。在promise外抛出异常。

    const promise = new Promise(function (resolve, reject) {
        resolve('ok');
        setTimeout(function () { throw new Error('外部错误') }, 0);
    });
    promise.then(
        (data) => console.log('data-->',data)
    ).catch(err => console.log('err-->',err);
    // 运行结果如下:
    data-->ok
    Uncaught Error: 外部错误

    3. Promise内部抛出异常后,即使不捕获也不会导致代码的终止运行。

    即Promise内部的异常只在Promise内部。

    new Promise((resolve,reject)=> {
        throw new Error("err")
        // 相当于
        try {
           throw new Error("err")
        } catch(err) {
           reject(err)
        }
    }); // 错误不会终止代码
    console.log("会执行"); 
    // 执行结果如下;错误信息会打印
    "会执行"
    Uncaught (in promise) Error: err 

    4. catch方法返回一个promise, 且将状态由rejected变为resolved。catch方法的返回值将作为后续回调函数的参数。

    所以后面可以接着调用then方法。

    const promise = new Promise(function (resolve, reject) {
        throw new Error("错误")
    });
    promise
    .then(data => console.log('data-->',data))
    .catch(err => {
    console.log('Caught err-->',err);
    return err;
    // 相当于resolve(err)
    }).then((a)
    => console.log('go on-->',a)); // 运行结果如下: Caught err-->Error: 错误 go on-->Error: 错误

    3. Promise.prototype.finally()

    不管状态(不能是pending)如何,都会执行的方法。语法:

    new Promise((resolve,reject) => {
        // 状态修改为resolved或者rejected
    }).finally(() => {...} )

    finally方法最后返回一个promise实例,回调函数不接受任何值,return方法也被忽略。

    但是会默认返回之前的实例对象传递的参数。

    本质上,finally也是then的特例。

    Promise.prototype.finally = function(callback) {
       const P = this.constructor;
       return this.then(
           (value) => P.resolve(callback()).then(() => value),
           (reason) => P.resolve(callback()).then(() =>{ throw reason})
       )
    }

    示例:

    const p = new Promise((resolve,reject) => {
        resolve(2)
    }).finally((data) => {
        console.log(data); //undefined 不接受任何参数
        return 5;
    }).then(after => {
        console.log(after);  // 2,之前的参数
    });

    应用:

    如处理文件完成后,不论成功与否都关闭文件。

    三. 静态方法

    1.Promise.resolve()

    该方法将对象或者非对象转为resolved(fulfilled)状态的promise对象。语法如下:

    Promise.resolve(data);
    // 相当于;
    new Promise(function(resolve, reject){
       resolve(data);
    })

    1. 没有参数时

    直接返回一个resolve状态的promise对象。then方法会立即执行。

    Promise.resolve().then(
       result => console.log(result); // undefined
    )

    2. 参数是一个promise对象,不做任何处理

    返回参数对象自身;此时和new Promise的写法运行结果不同。

    const p = Promise.resolve(); 
    const p1 = Promise.resolve(p); //就是p
    const p2 = new Promise(res => res(p)); //新建一个对象,对象状态依赖p
    // res(p)可以看作 await p; await fulfilled; 
    
    // 首先;p1 === p; p2!===p
    console.log(p === p1); // true
    console.log(p === p2); // false
    /******第一种情况***********/
    p1.then(() => {
        console.log('p1-1');
    }).then(() => {
        console.log('p1-2');
    })
    p.then(() => {
        console.log('p-1');
    }).then(() => {
        console.log('p-2');
    })
    // 运行结果如下:
    p1-1
    p-1
    p1-2
    p-2
    /*******第二种情况********/
    p2.then(() => {
        console.log('p2-1');
    }).then(() => {
        console.log('p2-2');
    }).then(() => {
        console.log('p2-3');
    })
    p.then(() => {
        console.log('p-1');
    }).then(() => {
        console.log('p-2');
    }).then(() => {
        console.log('p-3');
    })
    // 运行结果如下
    p-1
    p-2
    p2-1
    p-3
    p2-2
    p2-3

    3. 参数是一个普通的对象(不含then方法) / 原始值 / 单独的then方法

    返回一个promise对象,且resolve的参数是被处理的参数

    Promise.resolve('hello world').then(result => console.log(result));
    Promise.resolve({a: 'hello'}).then(result => console.log(result));
    function then(resolve, reject) {
       consoel.log('test'); // 不会执行
       resolve('fn');
    }
    Promise.resolve(then).then(result => console.log('fn--',result))
    
    // 运行结果如下:
    hello world
    {a: 'hello'}
    fn--function then(resolve,reject){resolve('fn')}

    4.参数是一个带有普通then方法的对象

    返回一个状态是pending的promise对象。不会触发后面的then方法的回调函数。

    const obj = {
        then() { console.log('then') }
    }
    Promise.resolve(obj).then(result => console.log('obj1-->',result));
    // 相当于
    new Promise((resolve,reject) => {
       // obj含有then方法
       resolve(obj); // 相当于obj.then()立即执行,后面没有处理状态的函数
    })

    运行结果:只打印then

    5. 参数对象的then方法是一个带有函数参数的方法

    因为对象的then方法默认立即执行,系统默认将resolve,reject作为参数传入对象的then方法。

    即第一个参数是resolve,第二个参数是reject。

    • 如果对象的then方法,没有调用函数参数,相当于普通then方法,返回一个pending状态的promise对象
    • 如果调用了then方法中的函数参数,返回一个promise对象,函数参数的数据是promise对象返回的数据
    const thenable = {
        then(a,b) {
            console.log('console-thenable');
            a('ok');
        }
    }
    Promise.resolve(thenable)
    .then(result => console.log('thenable1-->',result))
    .catch(err => console.log('err1-->', err))
    // 相当于
    new Promise((resolve,reject) => {
        resolve(thenable); //相当于thenbale.then(resolve,reject)--> resolve('ok')
    }).then(result => console.log('thenable2-->', result))
    .catch(err => console.log('err-->',err));
    // 运行结果如下: console-thenable //立即执行的结果 console-thenable thenable1-->ok thenable2-->ok //如果修改obj的内容如下; const thenable = { then(a,b) { console.log('console-thenable'); b(new Error('err')); } } // 运行结果如下: console-thenable console-thenable err1-->Error: err err2-->Error: err

    应用: 

    将函数转为异步操作

    function getData() {
        return 5;
    }
    // 要求在返回数据后进行后续操作
    Promise.resolve(getData()).then(data => {
        console.log(data); //5
    })

    2. Promise.reject()

    返回一个promise实例,实例状态为rejected。语法:

    Promise.reject(obj);
    // 相当于
    new Promise(function(resolve,reject) {
       reject(obj)
    })

    即使遇到含有then方法的对象是处理结果和Promise.resolve()不同。

    // .catch
    const obj = {
        thenable: (res,rej) => {
            rej('err');
        }
    }
    Promise.reject(obj).catch(err => {
        console.log(err); //err不是rej的参数,而是整个obj对象{thenable: f}
    });

    3. Promise.all()

    接受一个数组(每个参数都是promise实例)或者 Iterator接口(每个返回值都是promise实例)作为参数。

    如果参数不是promise实例,系统会自动调用Promise.resolve()方法,将参数转为promise实例。

    const arr = [1,2];
    Promise.all(arr) // 相当于 arr.map(i => Promise.resolve(i))
    .then(data => console.log('data-->',data))
    /****运行结果如下***/
    // data--> [1,2]

    返回一个新的Promise实例。

    const promises = [1,3,4,5].map(item => Promise.resolve(item));
    const newP = Promise.all(promises); //生成新的实例

    状态由参数中的每个实例共同决定。

    1)如果新的实例状态为fulfilled,  则要求所有的参数实例的状态都是fulfilled。

    此时,每个实例的返回值组成一个数组传递给回调函数。

    const promises = [1,3,4,5]; // 自动转promise
    const newP = Promise.all(promises); //生成新的实例
    // promises对应实例对象全部都是fulfilled状态
    newP.then(result => console.log('result-->', result)); 
    // 也可以使用参数解构
    newP.then(([p1,p2,p3,p4]) => console.log(p1,p2,p3,p4));
    //运行结果如下:
    result-->[1,3,4,5]
    1 3 4 5

    2) 如果新的实例状态为rejected,只要参数实例有一个被rejected,就会触发新实例的回调函数。

    且被rejected实例的返回值会作为回调函数的参数。

    const promises1 = [1,3,4,5].map(item => Promise.resolve(item));
    const promises2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('错误'))
        }, 5000); // 该setTimeout虽然是异步任务,但是promise还未执行完,所以还是promise内部的异常。区分
    })
    const newP = Promise.all([...promises1, promises2]); //生成新的实例
    newP.then(result => console.log('result-->', result))
    .catch(err => console.log('err-->', err));
    
    // 运行结果如下:
    err-->Error: 错误 // 5秒中后打印

    3)如果参数中的实例自己有catch方法,那么不会触发新实例的catch方法

    // 当参数实例没有catch方法,捕获第一个错误
    const p1 = Promise.reject('err1');
    const p2 = Promise.reject('err2');
    Promise.all([p1,p2])
    .then(data => console.log('data-->',data))
    .catch(err => console.log('err-->',err)); // err1  catch方法返回的Promise对象是resolved状态
    /***运行结果如下***/
    err-->err1
    
    // 当参数实例自己有catch方法
    const p1 = Promise.reject(new Error('err1')).catch(err => err);
    const p2 = Promise.reject(new Error('err2')).catch(err => 5);
    Promise.all([p1,p2])
    .then(data => console.log('data-->',data))
    .catch(err => console.log('err-->',err));
    /****运行结果如下***/
    data--> [Error: err1, 5]

    4. Promise.race()

    参数性质和Promise.all()一样,都是以参数为Promise实例的数组为参数。如果不是实例,自动转为实例。

    返回一个新的Promise实例。

    作用不一样:

    只要有一个参数实例的状态发生改变,新的实例的状态随之改变,参数实例的返回值作为对应的回调函数的参数。

    应用:用来设置超时时间

    function fetch(url) {
        return new Promise((resolve,reject) => {
            setTimeout(() => {
                // 伪代码,2秒钟才能拿到结果
                resolve('data')
            }, 2000)
        })
    }
    const p1 = fetch();
    const p2 = new Promise((resolve,reject) => {
        setTimeout(() => {
            reject(new Error("请求超时"));
        }, 500);  // 相当于设置超时时间为500ms
    });
    Promise.race([p1,p2])
    .then(data => console.log('data-->',data))
    .catch(err => console.log('err-->',err))
    
    // 运行结果如下
    err--> Error: 请求超时  //500ms后打印

    5. Promise.allSelected() ES2020:浏览器支持

    参数同上,返回一个新的Promise对象。

    该方法等待所有的参数实例对象状态全部改变(返回结果),才会触发回调。

    且新的对象的状态只有fulfilled一个状态。

    回调函数的参数是一组对象组成的数组:

    // 主要有两种
    [
       {status: 'fulfilled', value: 'data'}, // 成功
       {status: 'rejected', reason: Error: 请求超时} // 失败
       ....
    ]

    示例:

    function fetch(url) {
        return new Promise((resolve,reject) => {
            setTimeout(() => {
                // 伪代码,2秒钟才能拿到结果
                resolve('data')
            }, 2000)
        })
    }
    const p1 = fetch();
    const p2 = new Promise((resolve,reject) => {
        setTimeout(() => {
            reject(new Error("请求超时"));
        }, 500);  // 相当于设置超时时间为500ms
    });
    Promise.allSettled([p1,p2])
    .then(data => console.log('data-->',data))
    .catch(err => console.log('err-->',err))
    
    // 运行结果如下:
    data -->[{status: 'fulfilled', value: 'data'}, {status: 'rejected', reason: Error: 请求超时}] //2秒后打印

    应用:

    不关心运行结果,只关心是否运行结束时

    Promise.allSettled([p1,p2,p3]).then(result => {
       //只要触发回调函数,就证明所有的参数实例全部运行完成
    })

    6. Promise.any() 提案

    参数同上。返回一个新的Promise对象。

    只要有一个状态变为fulfilled,新的对象状态变为fulfilled,触发回调;

    所有的状态变为rejected,新的对象的状态才变为rejected,触发catch回调。

    回调函数的参数是一个所有实例返回错误结果的数组。

    7. Promise.try()提案

    参数是一个函数。用于统一同步函数和异步函数的处理机制。

    fn是同步函数,就按照同步函数的规则运行;如果fn是异步函数,就按照异步函数运行。

    Promise.try(fn)
    .then()
    .catch()
  • 相关阅读:
    java 8新特性 匿名内部类的使用
    java 8新特性
    jmeter 性能测试
    idea 背景颜色设置
    SpringBoot yaml的配置及使用
    idea 类图显示
    SpringSecurity 获取认证信息 和 认证实现
    MySQL-慢查询日志
    微信小程序领取卡券
    ThinkPhp5-PHPExcel导出|导入 数据
  • 原文地址:https://www.cnblogs.com/lyraLee/p/10091327.html
Copyright © 2020-2023  润新知