众所周知 Javascript
是单线程模型语言,同时只能执行一个任务,其余任务都必须在后面排队等待。git
所以,异步处理就成为 Javascript
处理多任务时提高效率最重要的方式之一,这也是 Javascript
区别于其余语言的重要特征。github
本文将经过对比三种目前最流行的异步处理方式,让读者深入体会不一样的异步处理方式的优缺点,进一步感觉 Javascript
异步处理的独特魅力。shell
首先登场的是 callback
(回调函数),它是异步操做最基本的方法。编程
callback
的具体介绍以下。json
下面是两个函数f1
和f2
,编程的意图是f2
必须等到f1
执行完成,才能执行。promise
function f1() {
// ...
}
function f2() {
// ...
}
f1();
f2();
复制代码
上面代码的问题在于,若是f1
是异步操做,f2
会当即执行,不会等到f1
结束再执行。bash
这时,能够考虑改写f1
,把f2
写成f1
的回调函数。服务器
function f1(callback) {
// ...
callback();
}
function f2() {
// ...
}
f1(f2);
复制代码
回调函数的优势是简单、容易理解和实现,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,使得程序结构混乱、流程难以追踪(尤为是多个回调函数嵌套的状况),并且每一个任务只能指定一个回调函数。异步
二号选手是 Promise
,它是异步编程的一种新的解决方案。async
所谓
Promise
,简单说就是一个容器,里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果。从语法上说,Promise
是一个对象,从它能够获取异步操做的消息。Promise
提供统一的API
,各类异步操做均可以用一样的方法进行处理。
下面是一个Promise
对象的简单例子。
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}
timeout(100).then((value) => {
console.log(value);
});
复制代码
Promise
解决了callbak
回调地狱的问题,异步处理的表达流程也更加清晰,可是多层嵌套带来的繁琐写法,也是困扰Promise
的难点。
最后一位选手是async
,async
函数是什么?一句话,它就是 Generator
函数的语法糖。
具体代码展现以下。
const fs = require('fs');
const readFile = function (fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function(error, data) {
if (error) return reject(error);
resolve(data);
});
});
};
const asyncReadFile = async function () {
const f1 = await readFile('/etc/fstab');
const f2 = await readFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
复制代码
async
函数的最大特色,就是能够将异步处理编写成同步处理,可读性是最强的。相应的,它对开发者的理解难度和技能要求也是最高的。
经过js
代码依次读取files
文件夹中的三个文件——a.json
、b.json
、c.json
的内容。
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"
}
复制代码
首先读取a.json
的内容,而后获取next
再读取b.json
,最后读取b.json
的next
获取c.json
,这是最典型的js
异步处理的问题之一。
Node.js
是服务器端运行Javascript
的重要环境,能够为js
提供读取文件的接口,所以,本次比赛也将在Node.js
(8.0以上)的环境下进行。
Node.js
提供的比赛工具以下:
const fs = require('fs') // 文件读写接口,获取json文件的内容
const path = require('path') // 文件路径接口,获取files文件夹里面三个文件的文件路径
复制代码
首先是callback
登场,它给出的解决方式以下:
// callback 方式获取一个文件的内容
function getFileContentByCallback(fileName, callback) {
// path 获取文件路径
const fullFileName = path.resolve(__dirname, 'files', fileName)
// fs 读取文件
fs.readFile(fullFileName, (err, data) => {
if (err) {
console.error(err)
return
}
callback(
JSON.parse(data.toString())
)
})
}
// 读取文件
getFileContentByCallback('a.json', aData => {
console.log('a data callback: ', aData)
getFileContentByCallback(aData.next, bData => {
console.log('b data callback: ', bData)
getFileContentByCallback(bData.next, cData => {
console.log('c data callback: ', cData)
})
})
})
// 输出结果
a data callback: { next: 'b.json', msg: 'this is a' }
b data callback: { next: 'c.json', msg: 'this is b' }
c data callback: { next: null, msg: 'this is c' }
复制代码
接下来是promise
登场,它的解决方式以下:
// promise 方式获取一个文件的内容
function getFileContentByPromise(fileName) {
const promise = new Promise((resolve, reject) => {
// path 获取文件路径
const fullFileName = path.resolve(__dirname, 'files', fileName)
// fs 读取文件
fs.readFile(fullFileName, (err, data) => {
if (err) {
reject(err)
return
}
resolve(
JSON.parse(data.toString())
)
})
})
return promise
}
// 读取文件
getFileContentByPromise('a.json').then(aData => {
console.log('a data promise: ', aData)
return getFileContentByPromise(aData.next)
}).then(bData => {
console.log('b data promise: ', bData)
return getFileContentByPromise(bData.next)
}).then(cData => {
console.log('c data promise: ', cData)
})
// 输出结果
a data promise: { next: 'b.json', msg: 'this is a' }
b data promise: { next: 'c.json', msg: 'this is b' }
c data promise: { next: null, msg: 'this is c' }
复制代码
最后登场的是async
,它的解决方式以下:
// async 方式获取一个文件的内容
async function getFileContentByAsync(fileName) {
try {
// getFileContentByPromise 是上面promise获取文件内容的处理函数,aysnc直接调用
const aData = await getFileContentByPromise(fileName)
const bData = await getFileContentByPromise(aData.next)
const cData = await getFileContentByPromise(bData.next)
console.log('a data async: ', aData)
console.log('b data async: ', bData)
console.log('c data async: ', cData)
} catch (error) {
console.error(error)
}
}
// 读取文件
getFileContentByAsync('a.json')
// 输出结果
a data async: { next: 'b.json', msg: 'this is a' }
b data async: { next: 'c.json', msg: 'this is b' }
c data async: { next: null, msg: 'this is c' }
复制代码
从比赛结果来看,它们均可以顺利完成任务,从实现方式来看,却各不相同。
本次比赛并不是比较三者到底谁更强,而是经过最直观的对比方式,让你们感觉js
异步处理的多样性,以及每种异步处理的特色和差别性。
callback
利于理解、学习成本低,Promise
承上启下、中流砥柱,async
集大成者、表明将来。
最后附上比赛的GitHub地址:github.com/jiangjiahen…
祝工做顺利,生活幸福。