Promise是ES6中提供的一个API函数对象,可以来解决异步编程中遇到的回调地狱问题。
(Promise
的作用是解决回调金字塔的问题,对于控制异步流程实际上没有起到很大的作用。真正使用Promise
对异步流程进行控制,我们还要借助ES6 generator
函数。Generator 函数在Promise的基础上加了一个执行器,但是Generator语法也麻烦,不直观)
Promise
的兴起,是因为异步方法调用中,往往会出现回调函数一环扣一环的情况。这种情况导致了回调金字塔问题的出现。不仅代码写起来费劲又不美观,而且问题复杂的时候,阅读代码的人也难以理解。 代码不好阅读也不好维护。
举例如下:
db.save(data, function(data){ // do something... db.save(data1, function(data){ // do something... db.save(data2, function(data){ // do something... done(data3); // 返回数据 }) }); });
假设有一个数据库保存操作,一次请求需要在三个表中保存三次数据。那么我们的代码就跟上面的代码相似了。这时候假设在第二个db.save
出了问题怎么办?基于这个考虑,我们又需要在每一层回调中使用类似try...catch
这样的逻辑。这个就是万恶的来源,也是node刚开始广为诟病的一点。
另外一个缺点就是,假设我们的三次保存之间并没有前后依赖关系,我们仍然需要等待前面的函数执行完毕, 才能执行下一步,而无法三个保存并行,之后返回一个三个保存过后需要的结果。
解决回调深渊的利器Promise
1.new 出 Promise对象
2.往promise对象中放一个异步任务
3.默认异步任务就要去做了,状态为Pending(正在进行)
4.当异步任务执行成功,状态为Resolved,异步任务失败,状态为Rejected
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
// 创建了一个promise对象 //只要new 出来 这个参数函数会马上执行 // resolve 表示成功的方法 reject表示失败的方法 const p = new Promise(function(resolve,reject){ fs.readFile('./a.txt','utf8',(err,data) => { if(err){ // throw err 如果失败,调失败的方法 把错误对象传出去 reject(err) } else { // 如果成功,调成功的方法 把成功后的数据传出去 resolve(data) } }) }) // 使用promise对象的then方法来获取承诺的结果 // then 方法的第一个参数是resolve方法 第二个参数是reject方法 // then方法可以继续往后.then p.then(function(data){ console.log(data) },function(err){ // 该方法是Promise内部的reject方法 console.log(err) })
promise解决回调地狱
//封装一个Promise风格的读取文件(这里主要是学习then方法可以继续往后.then) pReadFile = function (...args) { // ...在参数中表示剩余参数 /*console.log(args) console.log(...args)*/ // 这里的...是展开数组的意思 return new Promise(function (resolve, reject) { fs.readFile(...args, (err, data) => { if (err) { // throw err 如果失败,调失败的方法 把错误对象传出去 return reject(err) } else { // 如果成功,调成功的方法 把成功后的数据传出去 resolve(data) } }) }) } // then 方法可以继续往后 then // 可以顺序的 .then 然后指定下一个任务 // 第一个 then 的函数是 Promise 容器的 resolve 函数 // then 方法中的返回值将传递给下一个 then 方法 // then 方法中返回一般的数据没有意义 // 只有当 then 方法返回一个 Promise 对象的时候才有意义 // 当我们的 then 方法返回一个 Promise 对象的时候,那么下一个 then 函数就会作为该 Promise 对象的 resolve 方法 pReadFile('./a.txt','utf8') .then((data) => { console.log(data) return pReadFile('./b.txt','utf8') }) .then((data) => { console.log(data) return pReadFile('./c.txt','utf8') }) .then((data) => { console.log(data) })
```javascript .catch(err => { console.log('catch err', err) }) ```
我们发现 Promise 也没有想象中的那么好用 还是会出现一堆繁琐的 then 甚至语法不太好理解,尤其是新手。 所以 ECMAScript 2017 标准中制定发布了一个新的方式: 终极的异步编程解决方案:async 函数 有了 async 函数,本质还是异步,但是我们可以像写同步代码一样来写异步代码
注意:await后面必须是一个promise对象await 会等待后面 Promise 的结果。async 函数返回值 返回 Promise 对象,如果非 Promise对象,那么会将返回值包装为一个立即 resolve 的 Promise 对象返回由于 async 函数返回的是 Promise 对象,那么我们可以继续在其它的 async 函数中使用 await 来等待 async 函数的结果
async function main(argument) { console.log(2) // await 命名后面跟一个 Promise 任务 // dataA 就是 await 后面的 Promise 任务的 resolve 方法的结果 // await 等待 Promise 内部 resolve const dataA = await pReadFile('./data/a.txt', 'utf8') console.log(3) console.log(dataA) const dataB = await pReadFile('./data/b.txt', 'utf8') console.log(4) console.log(dataB) const dataC = await pReadFile('./data/c.txt', 'utf8') console.log(5) console.log(dataC) } console.log(1) main() console.log(6)
结果为
1
2
6
3
dataA
4
dataB
5
dataC
所以是异步的,但是看起来像同步代码一样,调方法拿结果,因为没有回调