zoukankan      html  css  js  c++  java
  • 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()
  • 相关阅读:
    xls与csv文件的区别
    青音,经典爱情语录
    win7用户账户自动登录方法汇总
    How to using Procedure found Lead Blocker
    FTS(3) BSD 库函数手册 遍历文件夹(二)
    FTS(3) BSD 库函数手册 遍历文件夹(一)
    DisplayMetrics类 获取手机显示屏的基本信息 包括尺寸、密度、字体缩放等信息
    About App Distribution 关于应用发布
    FTS(3) 遍历文件夹实例
    OpenCV 2.1.0 with Visual Studio 2008
  • 原文地址:https://www.cnblogs.com/lyraLee/p/10091327.html
Copyright © 2011-2022 走看看