-
Notifications
You must be signed in to change notification settings - Fork 52
异步流程管理
LYF edited this page May 25, 2017
·
24 revisions
有一个url,存储在一个名为url.file
的文件内,内有一行json:
http://xxx.yyy.com/test.json
这个json的内容为:
{
"name": "李彦峰",
"age": 26
}
我想要先读取这个文件,然后下载这个url,并parse成一个对象,然后给age属性进行加1操作,然后在写回本地的json.file
文件里。
整个流程为:读 -> 下载 -> 解析 -> age加1 -> 写回
/**
点评:这种方式很啰嗦,代码量多,回调嵌套回调,可读性也不好。
这就是所谓的“回调地域”,好在我们parse的时候,是同步的。
不然还要在parse的回调中调用write
**/
const read = (file, cb) => {
fs.readFile(file, 'utf8', (err, url) => {
if(err){
console.error(err)
} else {
console.log('45:', url)
cb(url)
}
})
}
const download = (url, cb) => {
request(url, (err, response, json) => {
if (!err && response.statusCode === 200) {
cb(json)
} else {
console.error(err)
}
})
}
const parse = (res) => {
const obj = JSON.parse(res)
obj.age++
return JSON.stringify(obj)
}
const write = (text, file = './json.file') => {
fs.writeFile(file, text, 'utf8', (err) => {
console.error(err)
})
}
read('./url.file', (url) => {
download(url, (res) => {
const str = parse(res)
write(str)
})
})
Promise我们使用bluebird
,为了减少代码我没有使用 return new Promise
的方式,而是使用了 Promise.promisify
的方式,来promise化readFile/request/writeFile
这三个异步方法
/**
点评:这种方式相对于刀耕火种的回调函数方式就好了很多.
代码很易读,就跟写文章一样,then这个单词就是*然后*的意思,
我们可以看到代码的意思是:先读取`url.file`这个文件,然后下载,然后解析,然后写入。
流程非常清晰,非常有语义化。
最关键的是,异常处理也变得很方便。
**/
const read = (file) => Promise.promisify(fs.readFile)(file, 'utf8')
const download = (url) => Promise.promisify(request)(url)
const parse = (res) => {
const obj = JSON.parse(res.body)
obj.age++
return JSON.stringify(obj)
}
const write = (text, file = './json.file') => Promise.promisify(fs.writeFile)(file, text, 'utf8')
read('./url.file')
.then(download)
.then(parse)
.then(write)
.catch(e => console.error(e))
/**
点评:这种方式个人感觉相对于Promise的方式又好了一些.
把Promise的水平结构,拉成了垂直结构,跟传统的同步语言,比如Java、C等很像
**/
const read = (file) => Promise.promisify(fs.readFile)(file, 'utf8')
const download = (url) => Promise.promisify(request)(url)
const parse = (res) => {
const obj = JSON.parse(res.body)
obj.age++
return JSON.stringify(obj)
}
const write = (text, file = './json.file') => Promise.promisify(fs.writeFile)(file, text, 'utf8')
const flow = async (file) => {
const url = await read(file)
const res = await download(url)
const str = parse(res)
write(str)
}
flow('./url.file')
- async/await是写异步代码的新方式,以前的方法有回调函数和Promise。
- async/await是基于Promise实现的,它不能用于普通的回调函数。
- async/await与Promise一样,是非阻塞的。
- async/await使得异步代码看起来像同步代码,这正是它的魔力所在。
- async函数会隐式地返回一个promise,该promise的reosolve值就是函数return的值。
参考:http://www.cnblogs.com/fundebug/p/6667725.html
多个await命令后面的异步操作,如果不存在继发关系,最好让他们同时触发
let foo = await getFoo()
let bar = await getBar()
上面代码中,getFoo
和getBar
是两个独立的异步操作(即不互相依赖),被写成继发关系。这样比较耗时,因为只有在getFoo
完成以后,才会执行getBar
,完全可以让他们同时触发。
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()])
// 写法二
let fooPromise = getFoo()
let barPromise = getBar()
let foo = await fooPromise
let bar = await barPromise
---------测试
function getFoo () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('getFoo 执行了 ....')
}, 2000)
})
}
function getBar () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('getBar 执行了 ....')
}, 4000)
})
}
async function call () {
console.time('call\'s await')
const foo = await getFoo()
const bar = await getBar()
console.timeEnd('call\'s await')
console.log(foo)
console.log(bar)
}
async function call2 () {
console.time('call2\'s await')
const [foo, bar] = await Promise.all([getFoo(), getBar()])
console.timeEnd('call2\'s await')
console.log(foo)
console.log(bar)
}
async function call3 () {
console.time('call3\'s await')
const fooPromise = getFoo()
const barPromise = getBar()
const foo = await fooPromise
const bar = await barPromise
console.timeEnd('call3\'s await')
console.log(foo)
console.log(bar)
}
call()
call2()
call3()
测试环境:
Chrome 58
Node 7.10.0
打印信息:
call2's await: 4006.978ms
getFoo 执行了 ....
getBar 执行了 ....
call3's await: 4008.081ms
getFoo 执行了 ....
getBar 执行了 ....
call's await: 6008.222ms
getFoo 执行了 ....
getBar 执行了 ....
得出结论:
- 多个异步串行的话,则总时间为各个异步过程的用时的和
- 多个异步并行的话,则总时间为这些异步过程用时最长的那一个的时间
- 所以,如果没有下一步的异步要依赖上异步的结果这种情况,所有的await应该改为并行执行
- 出现上述的原因在于,浏览器和nodejs允许并行发送request http://blog.csdn.net/stevendbaguo/article/details/49620011