JavaScript 中回调地狱的今生前世
小作 2018-03-07 来源 :网络 阅读 830 评论 0

摘要:本篇JavaScript将讲解回调这一知识点,看完这篇文章会让你对回调的知识点有更加清晰的理解和运用。

本篇JavaScript将讲解回调这一知识点,看完这篇文章会让你对回调的知识点有更加清晰的理解和运用。

1. 讲个笑话

JavaScript 是一门编程语言

2. 异步编程

JavaScript 由于某种原因是被设计为单线程的,同时由于 JavaScript 在设计之初是用于浏览器的 GUI 编程,这也就需要线程不能进行阻塞。

所以在后续的发展过程中基本都采用异步非阻塞的编程模式。

简单来说,异步编程就是在执行一个指令之后不是马上得到结果,而是继续执行后面的指令,等到特定的事件触发后,才得到结果。

也正是因为这样,我们常常会说: JavaScript 是由事件驱动的。

3. 异步实现

用 JavaScript 构建一个应用的时候经常会遇到异步编程,不管是 Node 服务端还是 Web 前端。

那如何去进行异步编程呢?就目前的标准以及草案来看,主要有下面的几种方式:

· 回调

· promise

· Generator

· await/async

3.1 回调

这种异步的方式是最基础的实现,如果你曾经写过一点的 Node, 可能经常会遇到这样的代码:

connection.query(sql, (err, result) => {

    if(err) {

        console.err(err)

    } else {

        connection.query(sql, (err, result) => {

            if(err) {

                console.err(err)

            } else {

                ...

            }

        })

    }

})

如此,connection.query() 是一个异步的操作,我们在调用他的时候,不会马上得到结果,而是会继续执行后面的代码。这样,如果我们需要在查到结果之后才做某些事情的话,就需要把相关的代码写在回调里面,如果涉及到多个这样的异步操作,就势必会陷入到回调地狱中去。

这种回调地狱不仅看起来很不舒服,可读性比较差;除此之外还有比较重要的一点就是对异常的捕获无法支持。


3.2 Promise

Promise 是 ES 2015 原生支持的,他把原来嵌套的回调改为了级联的方式。

一般着,我们对一个 Promise 可以这样写:

var a = new Promise(function(resolve, reject) {

  setTimeout(function() {

      resolve('1')

  }, 2000)

})

a.then(function(val) {

    console.log(val)

})

如果要涉及到多个异步操作的顺序执行问题,我们可以这样写:

var a = new Promise(function(resolve, reject) {

  setTimeout(function() {

      resolve('1')

  }, 2000)

})

 

a

  .then(function(val){

    console.log(val)

    return new Promise(function(resolve, reject) {

      setTimeout(function() {

          resolve('2')

      }, 2000)

    })

  })

  .then(function(val) {

    console.log(val)

  })

也可以把函数抽离出来

var a = new Promise(function(resolve, reject) {

  setTimeout(function() {

      resolve('1')

  }, 2000)

})

function b(val) {

  console.log(val)

 

  return new Promise(function(resolve, reject) {

    setTimeout(function() {

        resolve('2')

    }, 2000)

  })

}

 

a.then(b).then(function(val) {console.log(val)

})

我们只需要 return 一个 Promise 即可实现这种多个异步操作的顺序执行。

粗略来看,这是一个比较优雅的异步解决方案了,并且在 Promise 中我们也可以实现分级的 catch。

但对于之前接触过其他语言的同学来说还是比较别扭的。那能否用同步的方式来书写异步呢?


3.3 Generator

在 ES 2015 中,出现了 Generator 的语法,熟悉 Python 的同学肯定对这种语法有点了解。

简单来说,Generator 可以理解为一个可以遍历的状态机,调用 next 就可以切换到下一个状态。

在 JavaScript 中,Generator 的 function 与 函数名之间有一个 *, 函数内部使用 yield 关键词,定义不同的状态。

先看一段代码:

function a() {

  return new Promise((resolve, reject) => {

    setTimeout(() => {

      resolve(1)

    }, 2000)

  });

};

var b = co(function *() {

    var val = yield a();

    console.log(val)

})

 

b()

上面的这段代码是借助 TJ 的 co 实现的,依照约定,co 中 yield 后面只能跟 Thunk 或者 Promise.

co 的实现代码很短,简单来说大体是这样:

// //www.alloyteam.com/2015/04/solve-callback-hell-with-generator/

function co(genFun) {

    // 通过调用生成器函数得到一个生成器

    var gen = genFun();

    return function(fn) {

        next();

        function next(err, res) {

            if(err) return fn(err);

            // 将res传给next,作为上一个yield的返回值

            var ret = gen.next(res);

            // 如果函数还没迭代玩,就继续迭代

            if(!ret.done) return ret.value(next);

            // 返回函数最后的值

            fn && fn(null, res);

        }

    }

}

简单来说就是一直借助 generator 的 next 进行迭代,直到完成这个异步操作才返回。当前人家官方的 co 是 200 行代码,支持异步操作的并行:

co(function *() {

    var val = yield [

        yield asyn1(),

        yield asyn2()

    ]

})()

但如果我们使用 co,强迫症们就会觉得这不是标准的写法,有点 hack 小子的感觉。

幸运的是,在 ES 2016 的草案中,终于提出了标准的写法。


3.4 await/async

这是在 ES 2016 中引入的新关键词,这将在语言层面彻底解决 JavaScript 的异步回调问题,目前可以借助 babel 在生产环境中使用。使用 await/async 可以让异步的操作以同步的方式来写。

使用方法和 co 非常类似,同时也支持同步写法的异常捕获。

function a() {

  return new Promise((resolve, reject) => {

    setTimeout(() => {

      resolve(1)

    }, 2000)

  })

}

var b = async function() {

  var val = await a()

  console.log(val)

}

 

b()

如果上述的代码完全用 Promise 实现,极有可能是下面的代码:

function a() {

  return new Promise((resolve, reject) => {

    setTimeout(() => {

      resolve(1);

    }, 2000);

  });

};

var b = function() {

  a().then(val) {

    console.log(val)

  }

  console.log(val)

};

 

b();

相比较来说,await/async 解决了完全使用 Promise 的一个极大痛点——不同Promise之间共享数据问题:

Promise 需要设定外层数据开始共享,这样就需要在每个then里面进行赋值,而 await/async 就不存在这样的问题,只需要以同步的方式去写就可以了。

await/async 对异常的支持也是特别好的:

function a() {

  return new Promise((resolve, reject) => {

    setTimeout(() => {

      resolve(1)

    }, 2000)

  });

};

var b = async function() {

    try {

        var val = await a()

        console.log(val)

    } catch (err) {

        console.log(err)

    }

};

 

b();

希望这篇文章可以帮助到你。总之,同学们,你想要的职坐标IT频道都能找到!

 


本文由 @小作 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved