异步编程是每一个运用 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 能够看看以下的文章:
应用 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)
}
}
感谢您的浏览,有不足之处请为我指出。
参考
本文同步于我的个人博客 http://blog.acwong.org/2017/06/24/javascript-async-programming/