es6-Promise
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了es6-Promise相关的知识,希望对你有一定的参考价值。
参考:阮一峰老师的 ES6 教程 Promise 篇
写一段传统的异步操作
还是拿之前讲 jquery deferred
对象时的那段setTimeout
程序
var wait = function () { var task = function () { console.log(‘执行完成‘) } setTimeout(task, 2000) } wait()
用Promise
进行封装
const wait = function () { // 定义一个 promise 对象 const promise = new Promise((resolve, reject) => { // 将之前的异步操作,包括到这个 new Promise 函数之内 const task = function () { console.log(‘执行完成‘) resolve() // callback 中去执行 resolve 或者 reject } setTimeout(task, 2000) }) // 返回 promise 对象 return promise }
- 将之前的异步操作那几行程序,用
new Promise((resolve,reject) => {.....})
包装起来,最后return
即可 - 异步操作的内部,在
callback
中执行resolve()
(表明成功了,失败的话执行reject
) wait()
返回的肯定是一个promise
对象,而promise
对象有then
属性。
const w = wait() w.then(() => { console.log(‘ok 1‘) }, () => { console.log(‘err 1‘) }).then(() => { console.log(‘ok 2‘) }, () => { console.log(‘err 2‘) })
then
还是和之前一样,接收两个参数(函数),第一个在成功时(触发resolve
)执行,第二个在失败时(触发reject
)时执行。而且,then
还可以进行链式操作。
以上就是 ES6 的Promise
的基本使用演示。
因为以下所有的代码都会用到Promise
,因此干脆在所有介绍之前,先封装一个Promise
,封装一次,为下面多次应用。
const fs = require(‘fs‘) const path = require(‘path‘) // 后面获取文件路径时候会用到 const readFilePromise = function (fileName) { return new Promise((resolve, reject) => { fs.readFile(fileName, (err, data) => { if (err) { reject(err) // 注意,这里执行 reject 是传递了参数,后面会有地方接收到这个参数 } else { resolve(data.toString()) // 注意,这里执行 resolve 时传递了参数,后面会有地方接收到这个参数 } }) }) }
参数传递
我们要使用上面封装的readFilePromise
读取一个 json 文件../data/data2.json
,这个文件内容非常简单:{"a":100, "b":200}
先将文件内容打印出来,代码如下。大家需要注意,readFilePromise
函数中,执行resolve(data.toString())
传递的参数内容,会被下面代码中的data
参数所接收到。
const fullFileName = path.resolve(__dirname, ‘../data/data2.json‘) const result = readFilePromise(fullFileName) result.then(data => { console.log(data) })
再加一个需求,在打印出文件内容之后,我还想看看a
属性的值,代码如下。之前我们已经知道then
可以执行链式操作,如果then
有多步骤的操作,那么前面步骤return
的值会被当做参数传递给后面步骤的函数,如下面代码中的a
就接收到了return JSON.parse(data).a
的值
const fullFileName = path.resolve(__dirname, ‘../data/data2.json‘) const result = readFilePromise(fullFileName) result.then(data => { // 第一步操作 console.log(data) return JSON.parse(data).a // 这里将 a 属性的值 return }).then(a => { // 第二步操作 console.log(a) // 这里可以获取上一步 return 过来的值 })
总结一下,这一段内容提到的“参数传递”其实有两个方面:
执行resolve
传递的值,会被第一个then
处理时接收到
- 执行
resolve
传递的值,会被第一个then
处理时接收到 - 如果
then
有链式操作,前面步骤返回的值,会被后面的步骤获取到
异常捕获
我们知道then
会接收两个参数(函数),第一个参数会在执行resolve
之后触发(还能传递参数),第二个参数会在执行reject
之后触发(其实也可以传递参数,和resolve
传递参数一样),但是上面的例子中,我们没有用到then
的第二个参数。这是为何呢 ———— 因为不建议这么用。
对于Promise
中的异常处理,我们建议用catch
方法,而不是then
的第二个参数。请看下面的代码,以及注释。
const fullFileName = path.resolve(__dirname, ‘../data/data2.json‘) const result = readFilePromise(fullFileName) result.then(data => { console.log(data) return JSON.parse(data).a }).then(a => { console.log(a) }).catch(err => { console.log(err.stack) // 这里的 catch 就能捕获 readFilePromise 中触发的 reject ,而且能接收 reject 传递的参数 })
在若干个then
串联之后,我们一般会在最后跟一个.catch
来捕获异常,而且执行reject
时传递的参数也会在catch
中获取到。这样做的好处是:
- 让程序看起来更加简洁,是一个串联的关系,没有分支(如果用
then
的两个参数,就会出现分支,影响阅读) - 看起来更像是
try - catch
的样子,更易理解
串联多个异步操作
如果现在有一个需求:先读取data2.json
的内容,当成功之后,再去读取data1.json
。这样的需求,如果用传统的callback
去实现,会变得很麻烦。而且,现在只是两个文件,如果是十几个文件这样做,写出来的代码就没法看了(臭名昭著的callback-hell
)。但是用刚刚学到的Promise
就可以轻松胜任这项工作
const fullFileName2 = path.resolve(__dirname, ‘../data/data2.json‘) const result2 = readFilePromise(fullFileName2) const fullFileName1 = path.resolve(__dirname, ‘../data/data1.json‘) const result1 = readFilePromise(fullFileName1) result2.then(data => { console.log(‘data2.json‘, data) return result1 // 此处只需返回读取 data1.json 的 Promise 即可 }).then(data => { console.log(‘data1.json‘, data) // data 即可接收到 data1.json 的内容 })
上文“参数传递”提到过,如果then
有链式操作,前面步骤返回的值,会被后面的步骤获取到。但是,如果前面步骤返回值是一个Promise
的话,情况就不一样了 ———— 如果前面返回的是Promise
对象,后面的then
将会被当做这个返回的Promise
的第一个then
来对待 ———— 如果你这句话看不懂,你需要将“参数传递”的示例代码和这里的示例代码联合起来对比着看,然后体会这句话的意思。
Promise.all
和Promise.race
的应用
我还得继续提出更加奇葩的需求,以演示Promise
的各个常用功能。如下需求:
读取两个文件data1.json
和data2.json
,现在我需要一起读取这两个文件,等待它们全部都被读取完,再做下一步的操作。此时需要用到Promise.all
// Promise.all 接收一个包含多个 promise 对象的数组 Promise.all([result1, result2]).then(datas => { // 接收到的 datas 是一个数组,依次包含了多个 promise 返回的内容 console.log(datas[0]) console.log(datas[1]) })
读取两个文件data1.json
和data2.json
,现在我需要一起读取这两个文件,但是只要有一个已经读取了,就可以进行下一步的操作。此时需要用到Promise.race
// Promise.race 接收一个包含多个 promise 对象的数组 Promise.race([result1, result2]).then(data => { // data 即最先执行完成的 promise 的返回值 console.log(data) })
Promise.resolve
的应用
从 jquery 引出,到此即将介绍完 ES6 的Promise
,现在我们再回归到 jquery 。
大家都是到 jquery v1.5 之后$.ajax()
返回的是一个deferred
对象,而这个deferred
对象和我们现在正在学习的Promise
对象已经很接近了,但是还不一样。那么 ———— deferred
对象能否转换成 ES6 的Promise
对象来使用??
答案是能!需要使用Promise.resolve
来实现这一功能,请看以下代码:
// 在浏览器环境下运行,而非 node 环境 cosnt jsPromise = Promise.resolve($.ajax(‘/whatever.json‘)) jsPromise.then(data => { // ... })
注意:这里的Promise.resolve
和文章最初readFilePromise
函数内部的resolve
函数可千万不要混了,完全是两码事儿。JS 基础好的同学一看就明白,而这里看不明白的同学,要特别注意。
实际上,并不是Promise.resolve
对 jquery 的deferred
对象做了特殊处理,而是Promise.resolve
能够将thenable
对象转换为Promise
对象。什么是thenable
对象?———— 看个例子
const thenable = { // 所谓 thenable 对象,就是具有 then 属性,而且属性值是如下格式函数的对象 then: (resolve, reject) => { resolve(200) } }
以上是关于es6-Promise的主要内容,如果未能解决你的问题,请参考以下文章