在上篇文章里《JavaScript基础——回调(callback)是什么?》咱们一块儿学习了回调,明白了回调就是一个在另一个函数执行完后要执行的函数,若是咱们但愿异步函数可以像同步函数那样顺序执行,只能嵌套使用回调函数,过多的回调嵌套会使得代码变得难以理解与维护,为了不“回调地狱”让人发狂的行为,ES6原生引入了Promise的模式,经过这种方式,让咱们代码看起来像同步代码,大大简化了异步编程,简直是ES6新特性中最让咱们兴奋的特性之一。ajax
什么是promise?编程
首先咱们看看promise这个单词的中文释义,做为名词解释为承诺、诺言、誓言、约言,从中文释义能够看出,是一个未发生,未来必定会发生的某种东东…… 接下来咱们来看看ECMA委员会怎么定义Promise的:数组
A Promise is an object that is used as a placeholder for the eventual results of a deferred (and possibly asynchronous) computation.promise
Promise是一个对象,用做占位符,用于延迟(多是异步)计算的最终结果。异步
简单的来讲,Promise就是装载将来值的容器。其实生活中有不少Promise的场景,设想如下的场景:咱们去快餐店点餐,你点了一份牛肉面,你扫码付款后,会拿到一份带订单号的收据。订单号就是快餐店给咱们的一份牛肉面的承诺(promise),保证了你会获得一份牛肉面。async
因此咱们必定要保管好咱们的订单收据,由于咱们知道了这个收据表明了咱们将来会有一份牛肉面,尽管快餐店不能立刻给咱们一份牛肉面,可是咱们大脑潜意识的把订单收据当作牛肉面的“占位符”了。ide
终于,咱们听到服务员在喊“100号的牛肉面好了,请到窗口取餐”,而后咱们拿着订单收据来到窗口递给服务员,咱们换来了牛肉面。异步编程
说了不少,简单描述这个概念就是一旦咱们须要的值准备好了,咱们就用对个人承诺值换取这个值自己。函数
可是,还有一种很差的结果,服务员叫到咱们的订单号,当咱们去拿的时候,服务员会一脸歉意的告诉咱们“十分抱歉,您的牛肉面卖完了”。做为顾客的咱们对这个状况,除了愤怒以外只能换个地吃饭了或者点其它的。从中咱们能够看出,将来值还有一个重要的特性:它可能成功也可能失败。post
生活的例子很简单,咱们都经历过,咱们是否是特别着急如何用Promise呢?在使用以前,咱们仍是先了解下——Promise State(承诺状态,注:暂且这么翻译,小编也不知道如何翻译更好)
Promise State(承诺状态)
Promise只会处在如下状态之一:
Pending(待处理): promise初始化的状态,正在运行,既未完成也没有失败的状态,此状态能够迁移至fulfilled和rejected状态。
Fulfilled(已完成):若是回调函数实现Promise的resolve回调(稍后介绍),那咱们的promise实现兑现。
Rejected(已拒绝):若是Promise调用过程当中遭到拒绝或者发生异常,那么咱们的promise被拒绝,处于Rejected(状态)。
Settled(不变的,暂且这么翻译):Promise若是不处在Pending状态,状态就会改变,要不是Fulfilled要不是Rejected这两种状态。
Promise的状态转换,能够用下面一张图进行表示(图片来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Methods)
Promise vs callback
好比咱们有个需求,须要经过AJAX实现三个请求,第二个和第三个请求都依赖上一个接口的请求,若是使用CallBack的方式,咱们的代码多是这样的:
ajaxCallajaxCallajaxCallconsole
你们很快就会发现,这种多重嵌套的代码不但难以理解,并且难以维护,这就是著名的“回调地狱”现象。
若是使用Promise则会让咱们的大脑更容易接受和理解,代码显得简单扁平化,代码调用以下,如何实现ajaxCallPromise稍后介绍:
ajaxCallPromiseajaxCallPromiseajaxCallPromiseconsole
你是否是以为代码的复杂性忽然下降,代码看起来更简单易读呢,你也许会问ajaxCallPromise怎么写?,别着急,接着往下看
Promise实现——(resolve, reject) 方法
要实现回调函数转换成Promise对象,咱们须要使用Promise构造函数,在上一小节,小编展现了ajaxCallPromise函数是如何调用的,ajaxCallPromise的实现内容以下,小编实现了(resolve,reject)相应的回调函数:
PromiseajaxAsyncWithNativeAPIresCode
如何理解这段代码呢?
首先定义ajaxCallPromise返回类型为Promise,这意味咱们会实现一个Promise的承诺。
Promise接受两个函数参数,resolve(成功实现承诺)和reject(异常或失败)
resolve和reject这两个特有的方法,会获取对应成功或失败的值
若是接口请求一切正常,咱们将会经过resolve函数接收返回的值
若是接口请求失败,咱们将会经过reject回调接收失败返回的值
再举个简单的例子,若是foo()和bar()函数都实现promise,咱们改怎么写呢?
方式一:
fooconsole
方式二(建议这种,简单易读)
fooconsole
.then(onFulfilled, onRejected)
Promise的then()方法容许咱们在任务完成后或拒绝失败后执行相应的任务,该任务能够是基于另一个事件或基于回调的异步操做。
Promise的then()方法接收两个参数,即onFulfilled 和 onRejected 的回调,若是Promise对象完成,若是成功状态则执行onFulfilled回调,若是异常或失败则执行onRejected回调。
简单的来讲,onFulfilled回调接收一个参数,及所谓的将来的值,一样 onRejected 也接收一个参数,显示拒绝的缘由。让咱们改动下上小节ajaxCallPromise的then()方法:
ajaxCallPromiseconsoleconsole
若是请求过程失败,第二个函数将会执行输出而不是第一个函数输出。
咱们一块儿再来看个简单的例子,咱们在setTimeout()实现Promise回调,代码以下:
Promiseconsole
这里咱们在这里实现了一个成功状态后没有返回成功状态值的Promise,函数执行后,成功返回后将来值将会是 undefined.
catch(onRejected)方法
除了then()方法能够处理错误和异常,使用Promise的catch()方法也能实现一样的功能,这个方法其实并无什么特别,只是更容易理解而已,咱们一眼就能明白是捕获异常的操做。
catch()方法只接收一个回调函数。catch()方法的onRejected回调的调用方式与then()方法的onRejected回调相同。
还记得咱们上小节ajaxCallPromise的then()方法的实现吗:
ajaxCallPromiseconsoleconsole
咱们还可使用catch()方法进行捕获异常或拒绝,效果是一致的。
ajaxCallPromisethenconsolecatchconsole
Promise.resolve(value)
Promise的resolve()方法接收成功返回值并返回一个Promise对象,用于将来值的传递,将值传递给.then(onFulfilled, onRejected) 的onFulfilled回调中。resolve()方法能够用于将将来值转化成Promise对象,下面的一段代码演示了如何使用Promise.resolve()方法:
p1 Promisep1consolePromisep1consolePromiseconsole
控制台将会输出如下内容:
Eden
Promise.reject(value)
Promise.reject(value)方法与上小节Promise.resolve(value)相似,惟一不一样的是将值传递给.then(onFulfilled, onRejected) 的onRejected回调中,同时Promise.reject(value)主要用来进行调试。
让咱们看看下面一段代码如何使用Promise.reject(value)方法:
p1 Promisep1consolePromiseconsole
控制台将输出:
Eden
Promise.all(iterable)
该方法传入迭代的Promise数组对象,并一块儿返回一个Promise对象,当全部的Promise迭代对象成功返回后,整个Promise才能返回成功状态的值。
好了,咱们一块儿看看怎么实现Promise.all(iterable) 方法:
p1 Promisep2 Promisearr p1p2Promisearrconsole
特别须要注意的一点,在迭代数组中,只要任意一个进入失败状态,那么该方法返回的对象也会进入失败状态,并将那个进入失败状态的错误信息做为本身的错误信息,示例代码以下:
p1 Promisep2 Promisearr p1p2Promisearrconsole
Promise.race(iterable)
与Promise.all(iterable) 不一样的是,Promise.race(iterable) 虽然也接收包含若干个Promise对象的可迭代对象,不一样的是这个方法会监听全部的Promise对象,并等待其中的第一个进入完成或失败状态的Promise对象,一旦有Promise对象知足,整个Promise对象将返回这个Promise对象的成功状态或失败状态,下面的示例展现了返回第一个成功状态的值:
p1 Promisep2 Promisearr p1p2Promisearrconsoleconsole
用Promise改写上篇文章回调
看过《JavaScript基础——回调(callback)是什么?》文章的同窗,文章的最后咱们用回调函数实现了一个真实的业务场景——用NodeJs实现从论坛帖子列表中显示其中的一个帖子的信息及留言列表信息,若是使用本篇文章学习到的内容,咱们如何实现呢, 代码以下:
index.js
fs path postsUrl path__dirnamecommentsUrl path__dirnamePromisefsJSONPromisedata datapostId postsUrlconsolecommentsUrlpostComments consolepostCommentsconsole