资讯专栏INFORMATION COLUMN

JavaScript 异步编程的四种方式

microelec / 1337人阅读

摘要:异步编程是每个使用编程的人都会遇到的问题,无论是前端的请求,或是的各种异步。本文就来总结一下常见的四种处理异步编程的方法。利用一种链式调用的方法来组织异步代码,可以将原来以回调函数形式调用的代码改为链式调用。

异步编程是每个使用 JavaScript 编程的人都会遇到的问题,无论是前端的 ajax 请求,或是 node 的各种异步 API。本文就来总结一下常见的四种处理异步编程的方法。

回调函数

使用回调函数是最常见的一种形式,下面来举几个例子:

// jQuery ajax
$.get("test.html", data => {
  $("#result").html(data)
})
// node 异步读取文件
const fs = require("fs")

fs.readFile("/etc/passwd", (err, data) => {
  if (err) {
    throw err
  }
  console.log(data)
})

回调函数非常容易理解,就是定义函数的时候将另一个函数(回调函数)作为参数传入定义的函数当中,当异步操作执行完毕后在执行该回调函数,从而可以确保接下来的操作在异步操作之后执行。

回调函数的缺点在于当需要执行多个异步操作的时候会将多个回调函数嵌套在一起,组成代码结构上混乱,被称为回调地狱(callback hell)。

func1(data0, data1 => {
  func2(data2, data3 => {
    func3(data3, data4 => data4)
  })
})
Promise

Promise 利用一种链式调用的方法来组织异步代码,可以将原来以回调函数形式调用的代码改为链式调用。

// jQuery ajax promise 方式
$.get("test.html")
  .then(data => $(data))
  .then($data => $data.find("#link").val("href"))
  .then(href => console.log(href))

自己定义一个 Promise 形式的函数在 ES6 当中也非常简单:

function ready() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("ready")
    }, 3000)
  })
}

ready().then(ready => console.log(`${ready} go!`))

在 node 8.0 以上的版本还可以利用 util.promisify 方法将回调形式的函数变为 Promise 形式。

const util = require("util")
const fs = require("fs")

const readPromise = util.promisify(fs.readFile)

readPromise("test.txt").then(data => console.log(data.toString()))

想详细了解 Promise 可以阅读拙作谈谈 ES6 的 Promise 对象。

Generators

node 的著名开发者 TJ 利用 ES6 新特性生成器(Generators)开发了一个异步控制工具 co。

如果不了解 Generators 可以看看以下的文章:

深入浅出ES6(三):生成器 Generators

深入浅出ES6(十一):生成器 Generators,续篇

利用 co 可以将异步代码的写法写成类似同步代码的形式:

const util = require("util")
const fs = require("fs")
const co = require("co")

const readFile = util.promisify(fs.readFile)

co(function* () {
  const txt = yield readFile("file1.txt", "utf8")
  console.log(txt)
  const txt2 = yield readFile("file2.txt", "utf8")
  console.log(txt2)
})

使用 Generators 的好似显然易见,可以使异步代码写得非常清晰,缺点就是要另外引入相关的库来利用该特性。

Async/Await

node7.6 以上的版本引入了一个 ES7 的新特性 Async/Await 是专门用于控制异步代码。先看一个例子:

const util = require("util")
const fs = require("fs")

const readFile = util.promisify(fs.readFile)

async function readFiles () {
  const txt = await readFile("file1.txt", "utf8")
  console.log(txt)
  const txt2 = await readFile("file2.txt", "utf8")
  console.log(txt2)
})

首先要使用 async 关键字定义一个包含异步代码的函数,在 Promise 形式的异步函数前面使用 await 关键字就可以将异步写成同步操作的形式。

看上去与 Generators 控制方式相差不大,但是 Async/Await 是原生用于控制异步,所以是比较推荐使用的。

错误处理

最后来探讨下四种异步控制方法的错误处理。

回调函数

回调函数错误处理非常简单,就是在回调函数中同时回传错误信息:

const fs = require("fs")

fs.readFile("file.txt", (err, data) => {
  if (err) {
    throw err
  }
  console.log(data)
})
Promise

Promise 在 then 方法之后使用一个 catch 方案来捕捉错误信息:

const fs = require("fs")
const readFile = util.promisify(fs.readFile)

readFile("file.txt")
  .then(data => console.log(data))
  .catch(err => console.log(err))
Generators 和 Async/Await

Generators 和 Async/Await 比较类似,可以有两种方式,第一种使用 Promise 的 catch 方法,第二种用 try catch 关键字。

Promise catch

const fs = require("fs")
const co = require("co")
const readFile = util.promisify(fs.readFile)

co(function* () {
  const data = yield readFile("file.txt").catch(err => console.log(err))
})
const fs = require("fs")
const co = require("co")
const readFile = util.promisify(fs.readFile)

async function testRead() {
  const data = await readFile("file.txt").catch(err => console.log(err))
}

try/catch

const fs = require("fs")
const co = require("co")
const readFile = util.promisify(fs.readFile)

co(function* () {
  try {
    const data = yield readFile("file.txt")
  } catch(err) {
    console.log(err)
  }
})
const fs = require("fs")
const readFile = util.promisify(fs.readFile)

async function testRead() {
  try {
    const data = await readFile("file.txt")
  } catch(err) {
    console.log(data)
  }
}

感谢您的阅读,有不足之处请为我指出。

参考

谈谈 ES6 的 Promise 对象

深入浅出ES6(三):生成器 Generators

深入浅出ES6(十一):生成器 Generators,续篇

本文同步于我的个人博客 http://blog.acwong.org/2017/06/24/javascript-async-programming/

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83718.html

相关文章

  • javasscript - 收藏集 - 掘金

    摘要:跨域请求详解从繁至简前端掘金什么是为什么要用是的一种使用模式,可用于解决主流浏览器的跨域数据访问的问题。异步编程入门道典型的面试题前端掘金在界中,开发人员的需求量一直居高不下。 jsonp 跨域请求详解——从繁至简 - 前端 - 掘金什么是jsonp?为什么要用jsonp?JSONP(JSON with Padding)是JSON的一种使用模式,可用于解决主流浏览器的跨域数据访问的问题...

    Rango 评论0 收藏0
  • Promise异步函数顺序执行四种方法

    摘要:前几天遇到一个编程题,要求控制顺序执行,今天总结了一下这个至少有好四种方法都可以实现,包括嵌套,通过一个串起来,,实现,以下逐一介绍。 前几天遇到一个编程题,要求控制promise顺序执行,今天总结了一下这个至少有好四种方法都可以实现,包括promise嵌套,通过一个promise串起来,generator,async实现,以下逐一介绍。原题目如下: //实现mergePromise函...

    Awbeci 评论0 收藏0
  • 深入理解js

    摘要:详解十大常用设计模式力荐深度好文深入理解大设计模式收集各种疑难杂症的问题集锦关于,工作和学习过程中遇到过许多问题,也解答过许多别人的问题。介绍了的内存管理。 延迟加载 (Lazyload) 三种实现方式 延迟加载也称为惰性加载,即在长网页中延迟加载图像。用户滚动到它们之前,视口外的图像不会加载。本文详细介绍了三种延迟加载的实现方式。 详解 Javascript十大常用设计模式 力荐~ ...

    caikeal 评论0 收藏0
  • Promise 四种常用方法。

    摘要:前言看到项目里不少人用了的库类,比如等方式,使用的时候翻看长长的文档,真心累觉不爱。用法常用三个场景。处理异步回调多个异步函数同步处理异步依赖异步回调封装统一的入口办法或者错误处理处理异步回调的基本用法,处理异步回调。 前言 看到项目里不少人用了Promise 的库类,比如 bluebird、q 、jQuery.Deffered 等 polyfill promise 方式,使用的时候...

    wangzy2019 评论0 收藏0

发表评论

0条评论

microelec

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<