JS中Promise理解与应用

Promise 中文释义:许诺,允诺;希望。意指我答应你我会去做的,不管成功还是失败我肯定会做的。

1、基础介绍

PromiseES6一个新的特性,本身是个对象用于表示一个异步操作的最终完成 (或失败), 及其结果值。创建一个Promise对象:

var promise1 = new Promise(function(resolve, reject) {}  );

传入一个参数-函数function(resolve, reject) {},该函数本身两个参数也是函数。Promise执行构造函数时立即执行该函数function(resolve, reject) {},然后返回对象promise1。执行结果要么成功、要么失败:

  • 如果执行完成调用resolve ,promise1状态变为fulfilled 。
  • 如果失败调用reject,promise1状态变为rejected 。

Demo1——一个简单示例

//Demo1
var promise1 = new Promise(function(resolve, reject) {
    //这里通常执行一个异步任务比如网络数据请求等,成功返回调用resolve , 失败reject
    resolve('执行成功');
});

promise1.then(function(s){
    console.log('成功:' + s);
} , function(s){
    console.log("失败:"  + s);
})

//结果输出 成功:执行成功

所以可以理解为promise用不同的状态来标记事件不同的处理结果,当执行完成时状态为fulfilled , 失败时状态变为rejected。然后通过then方法绑定事件来处理不同的状态。

Promise有三种状态

pending: 初始状态,既不是成功,也不是失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。

状态变化只能有 pending-> fulfilled 或者 pending-> rejected 两种方式,一旦状态发生变化则保持固定不变。

2、方法介绍

then方法 ,原型 then(onFulfilled[, onRejected])

如上所述通过then方法对promise对象绑定处理事件 ,它最多需要有两个参数:Promise 的成功和失败情况的回调函数。
当成功时执行第一个参数onFulfilled的回调处理,失败执行第二个参数onRejected的回调处理,然后返回一个 新的Promise对象,然后可以继续添加then方法处理回调,以此可形成链式调用。

then方法是个很重要的方法,下面我们结合各种情况来示例说明

注意两个参数是可选的
如果调用 then 的 Promise 的状态(fulfillment 或 rejection)发生改变,但是 then 中并没有关于这种状态的回调函数,那么 then 将创建一个没有经过回调函数处理的新 Promise 对象,这个新 Promise 只是简单地接受调用这个 then 的原 Promise 的终态作为它的终态。

Demo2——then方法没有回调处理参数(无任何参数)

//demo2---then没有回调处理参数
var promise1 = new Promise(function(resolve, reject) {
    //这里通常一个异步任务比如网络数据请求等,成功返回调用resolve , 失败reject
    resolve('执行成功');
});

var promise2 = promise1.then()
promise2.then(function(v){
    console.log("v: " + JSON.stringify(v));
})

//或直接这样
/*promise1.then().then(function(v){
    console.log("v: " + JSON.stringify(v));
})*/

//输出 v: "执行成功"

新生成的对象promise2 状态直接是完成状态,接收上一级传递的参数,然后执行then中成功回调处理。

如果执行的reject('失败'),则会抛出一个错误异常,需要catch方法来捕获。

关于then返回值

当一个 Promise 完成(fulfilled)或者失败(rejected)时,返回函数将被异步调用(由当前的线程循环来调度完成)。具体的返回值依据以下规则返回。如果 then 中的回调函数:

  • 1、返回了一个值,那么 then 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
  • 2、没有返回任何值,那么 then 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 undefined。
  • 3、抛出一个错误,那么 then 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
  • 4、返回一个已经是接受状态的 Promise,那么 then 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
  • 5、返回一个已经是拒绝状态的 Promise,那么 then 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
  • 6、返回一个未定状态(pending)的 Promise,那么 then 返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。

Demo3——then方法中直接返回一个值

    //demo3---then直接返回一个值
    var promise1 = new Promise(function(resolve, reject) {
        //这里通常一个异步任务比如网络数据请求等,成功返回调用resolve , 失败reject
        resolve('执行成功');
    });
    
    var promise2 = promise1.then(
        function(value){
            return value;
        }
    )
    
    promise2.then(function(v){
        console.log("--v: " + JSON.stringify(v));
    })
    //输出--v: "执行成功"

then 回调总会返回一个promise对象,这个对象可以是隐式自动生成的,也可以是我们显示创建的。然后根据不同的状态在执行相应的回调处理。

catch方法 ,原型 catch(onRejected)

用于处理promise失败时错误捕获

Demo4——catch方法

    //demo4---catch方法
    var promise1 = new Promise(function(resolve, reject) {
        //这里通常一个异步任务比如网络数据请求等,成功返回调用resolve , 失败reject
        reject('执行失败');
    });
    
    var promise2 = promise1.then(function(value){
        console.log("执行成功" + value);//这里不会走到这里
    }/*,function(value){//如果放开这里,下面的catch 不会执行
        console.log('失败:' + value);
    }*/
        
    ).catch(function(err){
        console.log('err:' + err);
        return 'err promise'
    })
    
    console.log(promise2);

执行结果


image.png

如果then中有reject处理失败的回调,则不会执行catch。其实它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。 (事实上, calling obj.catch(onRejected) 内部calls obj.then(undefined, onRejected)).

所以可以理解为 catch()等价于 then(undefined, onRejected)) , 但是使用catch来处理错误更合适。因为它也可以处理then成功回调处理中抛出的错误。

可以看出catch中本身返回一个promise对象且状态已完成。如果 catch中抛出一个错误或返回一个本身失败的 Promise , 通过 catch() 返回的Promise 被rejected;否则,它将显示为成功(resolved)。

Promise.reject(reason)

生成一个状态已失败的promise对象

Promise.resolve(value)

生成一个状态已完成的promise对象

all(iterable) 方法

Promise.all(iterable)

参数iterable :一个包含至少一个promise对象的数组。

类似于与操作,必须全部执行完成才触发接下来的操作。

方法返回一个新的 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

如果我们有多个异步任务处理,最后要监听全部执行完成,此时all方法就很合适。

race()

Promise.race(iterable)
参数iterable :一个包含至少一个promise对象的数组。

此类似于或操作,只要有一个完成或失败就算结束。任意一个子promise执行成功或失败后就会生成一个新的promise,状态就是第一个promise的状态。

Demo5—— race方法

    //race方法
    var promise1 = new Promise(function(resolve,reject){
        setTimeout(function(){
            resolve('promise1 完成');
        } , 3000);
    })
    
    var promise2 = new Promise(function(resolve,reject){
        setTimeout(function(){
            resolve('promise2 完成');
        } , 1500);
    })
    
    var promise3 = new Promise(function(resolve,reject){
        setTimeout(function(){
            resolve('promise3 完成');
        } , 2000);
    })
    
    Promise.race([promise1,promise2,promise3]).then(function(value){
        console.log("value: " + JSON.stringify(value));
    })


//输出 value: "promise2 完成"

promise2最先执行,所以其执行完成后就结束流程。

常用的方法基本以上几个,剩下的就是结合实际场景应用了。

3、Promise常见应用

Demo6—— promise添加多个方法

    var promise1 = new Promise(function(resolve,reject){
        resolve('执行完成');
    })
    
    promise1.then(function(value){
        console.log('1----' + value);
    })
    
    promise1.then(function(value){
        console.log('2----' + value);
    })
    
    promise1.then(function(value){
        console.log('3----' + value);
    })

输出:


image.png

对同一个promise对象添加多个处理方法,就是相当于同时监听一个事件。

Demo7 ——监听多个异步任务全部完成

    var promise1 = new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log('promise1 完成');
            resolve('promise1 完成');
        } , 3000);
    })
    
    var promise2 = new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log('promise2 完成');
            resolve('promise2 完成');
        } , 1500);
    })
    
    var promise3 = new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log('promise3 完成');
            resolve('promise3 完成');
        } , 2000);
    })
    
    Promise.all([promise1,promise2,promise3]).then(function(value){
        console.log("value: " + JSON.stringify(value));
    })

输出结果:


image.png

all方法返回新的promise对象,回调处理函数参数和实际执行完成顺序无关,只和添加的顺序有关。

Demo8 ——实现链式操作
特别是一个异步任务请求数据要用于下一个任务操作的这种必须的先后次序总,尤为重要。

修改demo7要顺序执行:即依次输出promise1完成-promise2完成-promise3完成。

    var promise1 = new Promise(function(resolve,reject){
        setTimeout(function(){
            console.log('promise1 完成');
            resolve('promise1 完成');
        } , 3000);
    }).then(function(value){
        return new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log('promise2 完成');
                resolve('promise2 完成');
            } , 1500);
        });
        
        return p
    }).then(function(value){
        return new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log('promise3 完成');
                resolve('promise3 完成');
            } , 2000);
        });
        
        return p
    })

此时promise1对象就是最后一个then方法返回的对象,状态为接受状态,并且该接受状态的回调函数的参数值为 promise3 完成

执行结果:

image.png

4、总结

  • promise一旦开始不能终止。
  • 状态一旦改变就固定了,不会在被修改。
  • 在异步操作中抛出错误异常无法被捕获。

Promise主要用于异步处理,根据不同的状态执行相应的回调处理,有点类似其他系统的状态机的概念

ok , 关于Promise的介绍到此结束,理解了这些在实际应用中为我们多任务的异步处理又多一种选择。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352