javascript回调地狱_技术 | JavaScript 解决回调地狱

JavaScript是单线程语言,但是js中有很多任务耗时比较长,比如ajax请求,如果都按照顺序进行,往往会出现浏览器无响应的情况,所以就需要异步的形式。我下面用Node.js中的读取文件来举例,node和JavaScript原理是差不多的。

先看看下面这个例子:

// a.json// {//   "next": "b.json",//   "msg": "this is A"// }const fs = require('fs')fs.readFile('a.json', (err, data) => {  if (err) {    console.error(err)    return  }  console.log(data.toString())})
读取文件内容是个非常简单的异步操作,你得在文件内容读出来之后才能对文件内容进行操作。这是读取一个文件内容的异步过程,只有一个异步函数,回调函数读出的内容也没有拿出来给其他函数使用。如果是几个异步函数连在一起呢?比如某一个读取文件的操作需要用到上一个读取文件读出来的内容。看下面这个例子:
// a.json{  "next": "b.json",  "msg": "this is A"}// b.json{  "next": "c.json",  "msg": "this is B"}// c.json{  "next": null,  "msg": "this is C"}const fs = require('fs')// 定义一个读取文件内容的函数function getFileContent (filename, callback) {  fs.readFile(filename, (err, data) => {    if (err) {      console.error(err)      return    }    // callback是一个函数,参数是文件的内容    callback(JSON.parse(data.toString()))  })}getFileContent('a.json', aData => { // aData中拿到a文件的内容,在这个函数里  console.log('a data is', aData)   // 才能调用aData.next访问b.json  getFileContent(aData.next, bData => {  // 用前面拿到的a文件内容里的b文件名拿到b文件内容,下面继续    console.log('b data is', bData)    getFileContent(bData.next, cData => {      console.log('c data is', cData)    })  })})
看这里的异步过程产生的回调嵌套已经有好几层了,如果再多几个文件,再多几个异步过程,如山一般的回调就会在代码里形成回调地狱。这个时候就轮到Promise来了。Promise 是异步编程的一种解决方案,它本身是一个对象,它代表了一个异步操作的最终完成的结果或者失败结果。promise本身是一个构造函数,下面代码创造了一个Promise实例:
const promise = new Promise((resolve, reject) => { // some code  if(/* 异步操作成功 */) {   resolve(value)  } else { /* 异步操作失败(不是抛出错误,是异步条件不满足的情况下reject) */    reject(error)  }})
promise接受两个参数,resolve, reject,这是两个js自带的函数,不用自己写。resolve的作用是将异步成功的内容传递出去,reject的作用相反,将异步失败时候的内容作为参数传递出去。promise实例生成后,就可以调用它的then和catch方法,在then中,我们可以拿到resolve和reject中返回的内容,而then又返回一个新的promise实例,这就是它的强大之处,举个例子就好懂了,下面用promise对上面读文件进行重写:
const fs = require('fs')// 获取文件的函数返回一个处理文件异步的promisefunction getFileContent (filename) {  const promise = new Promise((resolve, reject) => {    fs.readFile(filename, (err, data) => {      if (err) {        reject(err)        return      }      resolve(JSON.parse(data.toString())) // 将读取出来的文件内容用resolve传递出去    })  })  return promise}// 调用这个promise,在then里面拿到了a文件的内容getFileContent('a.json').then(aData => {  console.log('a Data ', aData)  // 返回一个新的promise,这个promise处理读取b文件的异步  return getFileContent(aData.next)}).then(bData => {  // 返回的是一个promise,所以可以直接链式调用then,这个then会拿到b的内容  console.log('b Data ', bData)  // 返回一个新的promise,这个promise处理读取c文件的异步  return getFileContent(bData.next)}).then(cData => {  // 链式调用  console.log('c Data ', cData)})
使用promise进行重写后,我们发现,再多的文件只不过是多几个链式调用而已,嵌套控制在一层,解决了回调地狱这个问题。在上面的链式调用里,后一个then会等待前一个promise的结果产生后调用,如果进行中报错,链式调用就会断掉,这时候就用到promise的另一个方法,catch:
promise  .then(...)  .then(...)  .then(...)  .catch(...) // catch可以处理前面所有then的报错,处理之后可以继续链式调用 .then(...)
一遇到异常抛出,Promise 链就会停下来,直接调用链式中的 catch 处理程序来继续当前执行。这上面的代码看起来或许有点像下面这样:
try {  let result1 = syncDoSomething1();  let result2 = syncDoSomething2(result1);  let result3 = syncDoSomething3(result2);} catch(error) {  handle(error) // 处理error}
哦,这样似乎有点同步任务的意思了,正好,在新的规定里可以用新的async/await 语法将这种类似同步编程处理异步逻辑的代码体现出来:
async function foo() {  try {   let result1 = await syncDoSomething1();   let result2 = await syncDoSomething2(result1);   let result3 = await syncDoSomething3(result2); } catch(error) {   handle(error) // 处理error }}
在async定义的函数中,你可以使用await语法来处理promise,await执行到这里之后,会等待promise中的异步处理完成之后直接取出promise的值,然后再继续执行。我们用这种方法再来优化一下我们开头说的读取文件的例子:
async function readFileData() {  const aData = await getFileContent('a.json')  console.log('a Data ', aData)  const bData = await getFileContent(aData.next)  console.log('b Data ', bData)  const cData = await getFileContent(bData.next)  console.log('c Data ', cData)}

是不是一下子就感觉这个代码非常简单了,就像是在写同步任务一样没有任何回调的顾虑,await会帮我们拿到promise的内容,直接用就可以了。到最后,什么回调地狱完全就没有了。

参考

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

END

91a66da4eff1c39b6964d92cb88e0655.png

↓↓↓ 三连一下,天天好心情!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值