ES6 Promise对象的基本使用

一. Javascript异步编程的解决方案

   说到Promise,必须先聊一聊异步编程。关于js的异步编程,传统解决方案主要有回调函数,事件监听,发布订阅模式,Promise等。本文将对前三种做简单介绍,详细介绍Promise。

1.回调函数

   作为异步编程的经典解决方案,js中回调函数无处不在。下面是一个经典例子:

    ajax(url, () => {
       // 处理逻辑
    })

   ajax请求得到结果后再执行回调函数中的代码,由此可以看出回调函数简洁直观。但当多个异步任务具有依赖性时,必须进行回调函数的嵌套,这将导致代码结构混乱,难以维护,因此也被称为回调地狱。

2.事件监听

   采用事件驱动模式,在这种模式下,异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。看一个例子

f1.on('done', f2);

   这个例子中,当f1 触发done事件时,f2立即执行。而done事件是在f1中定义的,f1执行完毕后会立即触done。事件监听的方式也很直观,且可以绑定多个事件,但缺点是整个程序都要变成事件驱动型,运行流程会变得不清晰。

3.发布订阅模式

    该模式的工作原理可以用一个很形象的例子说明。发布订阅模式有三个核心模块,发布者,订阅者,处理中心。这三者的关系就相当于杂志主编,报刊大厅,报刊读者的关系。 读者在报刊大厅订阅了一份杂志,而当该杂志的主编发布一期报刊时,大厅就会通知用户来拿新的报刊。
    显然,比起事件监听,发布订阅模式的优点在于可通过处理中心了解存在多少信号以及每个信号有多少订阅者等信息。Vue处理响应式数据的核心原理中就用到了这种模式。

4.Promise

   ES6开始提供了Promise对象,它是一种更合理更强大的异步编程解决方案

二. Promise对象

1. Promise是什么?

   通俗的讲,Promise对象可以理解为一个容器,容器里面存放着一个特殊的事件,该事件的执行需要一定的时间后才能有结果(一般是异步事件)。而Promise可以获取该结果,并根据这个结果改变自身状态,同时对不同的异步操作对外提供统一的api。
   之所以叫Promise(承诺)这个名字,是因为promise对象的状态只决定于异步操作的结果而与外界无关,一旦异步操作有了结果,Promise的状态也即确定了。任何其他操作都无法改变这个状态。这即是Promise名字的由来。

2. Promise的状态

   Promise对象有三种状态,他们分别是:

  • 1 pending:等待中,表示异步操作正在进行,还没有结果
  • 2 fulfilled 或 resolved:异步操作表示已成功,并得到了所期望的结果
  • 3 rejected:异步操作得到结果,但不是所期望的结果
    需要注意的是,Promise 的状态一旦改变,就永久保持该状态,不会再变了。且只有两种可能,即pending变为fulfilled和从pending变为rejected
2. Promise对象的基本用法
2.1 Promise对象的构造函数

   Promise对象的构造函数接受一个函数作为参数,而该函数的两个参数分别是resolve和reject。且它们又是两个函数,作用是修改promise对象的状态。
   resolve函数的作用是,将Promise对象的状态从 pending 变为 resolved,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
   reject函数的作用是,将Promise对象的状态即从 pending 变为 rejected,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

2.2 Promise.then()

   Promise实例具有then方法,它的作用是为promise对象状态改变时指定回调函数,换句话说,当promise对象状态改变时,会分别对应执行then方法中对应的回调函数。then方法有2个参数,第一个函数接收resolved状态的回调,第二个参数接收reject状态的回调。
   下面是一个简单例子

function fn(i){
    return new Promise((resolve, reject) => {
        if(i === 5)
        {
            resolve(i) //当调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数
                       // reject函数的参数通常是Error对象的实例,表示抛出的错误
        }
        else(
            reject(i)
        )
    }).then((value) =>{
        console.log(value +' i等于5') 
    }, (value) => {
        console.log(value +' i不等于5')
    })
}
fn(1); // i不等于5
fn(5); // i等于5

   需要注意的是,Promise对象的then方法返回的是一个新的Promise实例。之所以说是新的Promise实例,是指它不是原来的那个Promise实例。前面讲到Promise实例具有then方法。因此可以进行then方法的链式编程,即then方法后再接一个then方法。而then的链式编程又分两种情况。

   1. 前一个then的回调函数返回的是普通对象,则该对象会作为参数传入下一个then的回调函数。看下面例子:
 function fn(i){
    return new Promise((resolve, reject) => {
        if(i === 5)
        {
            resolve(i)               
        }
        else(
            reject(i)
        )
    })
}
fn(5).then((value) =>{
    return value+1
}).then((value) => {
    console.log(value)
});// 打印6 

   可以看到前一个then的回调返回了一个数值,该数值传入了下一个then方法的回调函数中

   2.前一个then的回调函数返回的是promise对象(包含异步操作),此时后一个then中的回调函数就会等待该promise的状态发生变化时再执行。由此即可以用then的链式编程指定一组按顺序调用的异步操作,解决回调地狱的问题。下面用一个例子演示
function fn(i){
    return new Promise((resolve, reject) => {
        if(i === 5)
        {
            resolve(i+1)            
        }
        else(
            reject(i)
        )
    })
}
fn(5).then(value => fn(value)) //第一个then方法返回了一个promise对象
.then(
    value => console.log(value),
    err => console.log(err,'i不等于5')
) // 打印 6 i不等于5

   可以看到前一个then的回调返回了一个promise,则第二个then等待该promise状态变为rejected时再执行相应回调函数。另外,这里还要注意不能混淆下面这两种说法:then方法返回一个promise实例 和 then方法的回调函返回值是primise对象

2.3 promise.catch()

   上面讲到,promise有三种状态。当状态变为resolved时,会调用then()方法指定的回调函数;而当异步操作抛出错误时,promise状态将会变为rejected,此时则会调用catch指定的回调函数。这里要说明一点,前面讲到promsie的then方法的第二个参数也可以指定rejected状态的回调函数,但一般地,我们通常使用catch方法来捕获错误,理由是catch方法不仅会捕获异步操作的错误,还会捕获then方法指定的回调函数运行过程中抛出的错误。还要注意的是当promise的状态已经变为rejected,再抛出错误是无法被catch方法捕获到的,因为前面讲到promise的状态一旦改变则将永久保持该状态。下面看一个例子

function fn(i){
    return new Promise((resolve, reject) => {
       resolve()
    })
}
fn(1).then( ()=> {
    throw new Error('error') // then方法抛出错误,会被catch捕获到
})
.catch( err => console.log(err))

2.4 Promise.finally()

   该方法无论Promise最后的状态如何,都会执行finally指定的回调函数。该方法不接受任何参数,这就意味着该方法的执行与Promise的状态无关。下面是一个例子,服务器使用 Promise 处理请求,然后使用finally方法关掉服务器。

server.listen(port)
  .then(function () {
    // ...
  })
  .finally(server.stop);
2.5 Promise.all()    Promise.race()

   这两个方法都接受一个由Promise实例组成的数组作为参数,且会将这些Promise实例包装成一个新的Promise实例,不同的是

   1. all方法 只有作为参数的几个Promise实例状态都变为fulfilled时,它才会变为fulfilled,并将他们的返回值成一个数组传递给自己的回调函数。看一个例子
function fn(i){
    return new Promise((resolve, reject) => {
        if(i === 5)
        {
            resolve(i+1)            
        }
        else(
            reject(i)
        )
    })
}
const list = [fn(5), fn(5), fn(5)]
Promise.all(list).then( value => {
    console.log(value) // 打印[ 6, 6, 6 ]
})
   2. all方法 当作为参数的几个Promise实例状态有一个变为rejected,则其状态立即变为rejected,且第一个状态变为rejected的Promise的返回值会传递给自身的回调函数,看下面例子
const list = [fn(5), fn(7), fn(5)]
Promise.all(list).then( value => {
    console.log(value) 
}).catch(err => {
    console.log(err) // 打印7
})

   3. race方法 只要作为参数的几个Promise的状态有一个率先改变状态,则其状态也立即变为相应状态并传递参数,看下面例子
const list1 = [fn(5), fn(5), fn(5)]
const list2 = [fn(7), fn(5), fn(5)]
Promise.race(list1).then( value => {
    console.log(value) // 打印6
})
Promise.race(list2).then( value => {
    console.log(value)
}).catch(err => {
    console.log(err) // 打印7
})
以上是Promise对象的基本使用,后面会继续分享一些Promise的经典应用及其底层实现原理。

参考:https://es6.ruanyifeng.com/#docs/promise

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

推荐阅读更多精彩内容