深入理解javascript异步模式
2016-12-13 12:51
627 查看
Callback 函数
当IO操作完成就回调函数,简单又熟悉fs.readFile('/path/to/file', (err, data) => { if (err) { return console.error('读取文件错误') } console.log(data) })
####回调陷阱
处理错误后忘记
return
fs.readFile('/path/to/file', (err, data) => { if (err) { // 文件不存在 /*return*/ console.error('文件没有找到') } console.log('找到文件后做某些操作!') })
使用不同的参数多次调用回调函数
function doThingAsync(cb) { fs.readFile('/path/to/file', (err, data) => { if (error) { cb(err) } if (data.toString() === 'special') { cb(null, '数据已找到') } cb(null, '没有数据special') }) } doThingAsync(function (err, data) { console.log('我会执行两次!') })
####解决回调陷阱
在任何时候回调之后就return
if (err) { return callback(err) } return callback(null, data)
保证执行一次
doMyAsyncThing(_.once((err, data) => { // 这个方法只可以执行一次, })
_.once由lodash提供
####回调提示
提高可读性,避免嵌套减少名称冲突
fetchUser(user_id, (err, user) => { fetchPosts(user_id, (err2, posts) => { fetchSomethingElse(user_id, (err3, other_stuff) => { if (err) { /* OOPS!应该使用 err3! */ } }) }) })
正确姿势
function onPostsRetrieved(err, posts) { /* ... 获取数据 */ } function onUserRetrieved(err, user) { fetchPosts(user.user_id, onPostsRetrieved) } fetchUser(user_id, onUserRetrieved)
##Promises
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果
Promise.resolve
创建指定resolved状态值的Promise
const foo = Promise.resolve('last') foo.then(x => { console.log('接着执行 ' + x) }) foo.then(x => { console.log('再接下来执行 ' + x) }) console.log('最先被执行')
Promise.reject
创建指定rejectd状态值的Promise
const foo = Promise.reject('rejection!') foo.then(x => { console.log('这里不会被执行,因为Promise进入rejected状态') }) foo.catch(x => { console.log(x) }) foo.catch(x => { console.log('Another ' + x) })
new Promise()
提供resolve和reject回调
new Promise((resolve, reject) => { fs.readFile('/path/to/file', (err, data) => { if (err) { return reject(err) } return resolve(data) }) }) .then(data => { console.log(data) }) .catch(err => { console.log(err) })
组合使用Promises
const promise1 = Promise.resolve(4); const promise2 = Promise.resolve(5); promise1.then(a => { /*return promise2.then(b => { return b+a; })*/ return {name:'test'}; }).then(c => { console.info(c); //{ name: 'test' } });
从then返回的任何值都会被添加到promise链
Promise chain
promiseA .then(() => throw new Error('Error')) // 假设 promiseB 抛出一个错误. .catch(err => console.log(err)) // C1 .then(() => promiseC) .then(() => promiseD) .catch(err => console.log(err)) // C2
promiseA .then(() => throw new Error('Error')) .catch(err => throw err) // C1 .then(() => promiseC) .then(() => promiseD) .catch(err => console.log(err)) // C2
Promise.all
Promise.all(promise集合) 方法返回一个promise,该promise会等promise集合内的所有promise都被resolve后被resolve,或以第一个promise被reject的原因而reject
const userService = require('./services/user') const users_promise = Promise.all([ userService.get(123), userService.get(555) ]) users_promise.then((users) => { let [user123,user555] = users; console.log('全部用户服务已经执行成功!') }) .catch((error) => { console.log('其中一个用户服务调用失败!') })
对于执行顺序不重要的任务可以使用
Promise.race
Promise.race(promise集合)方法返回一个promise,这个promise在promise集合中的任意一个promise被解决或拒绝后,立刻以相同的解决值被解决或以相同的拒绝原因被拒绝。
const timeout = new Promise((resolve, reject) => { setTimeout(reject, 10000) }) Promise.race([ timeout, userService.get(123) ]) .then(user => { console.log(user) }) .catch(error => { // 错误有可能来自timeout或者userService.get console.log(error) })
####Promise的陷阱
忘记处理错误
Promise.reject('original') .catch(err => { return Promise.resolve('default') }) .then(res => { console.log(res) // res -> 来自catch的返回值 })
Promise.reject('original') .then(res => { console.log(res) // 不会被执行 }) .catch(err => { return Promise.resolve('default') })
忘记返回Promise
Promise.resolve('/path/to/file') .then(filename => { /*return*/ new Promise((resolve, reject) => { fs.readFile(filename, (err, data) => { if(err) { return reject(err) } return resolve(data) }) }) }) .then(res => { console.log('result: ' + res) })
对event loop事件轮询的冲击
const userService = require('./services/user') // 如果user基数非常大 const users_to_fetch = [1, 2, 5, 19, ... 9999] const get_users_promises = _.map(users_to_fetch, (user_id) => { return userService.get(user_id) }) // 注意: 全部的Promise任务已经开始运行! Promise.all(get_users_promises)
解决event loop事件轮询的冲击
使用bluebird map方法
function timeoutPromise(timeout) { return new Promise(resolve => { setTimeout(resolve, timeout) }) } const things_to_do = new Array(100) // Promise.map 来自 bluebird (没有可用原生接口实现) -- 大概在两秒内完成 Promise.map(things_to_do, () => timeoutPromise(200), { concurrency: 10 }) .then(() => console.log('Batch complete!')) Promise.all(things_to_do.map(() => timeoutPromise(200))) .then(() => console.log('Flood complete!'))
##Generator
Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同
基本
function *oneGenerator() { const a = yield 1 const b = yield 1 console.log('result 1: ' + a) console.log('result 2: ' + b) } // 创建生成器实例 const generator = oneGenerator() //第一次输入将会被忽略 console.log(generator.next('a').value) console.log(generator.next('b').value) console.log(generator.next('c').value)
Generator不会阻塞事件轮询
生成无限序列或推迟计算
function *myGenerator() { let i = 1 while (true) { yield i++ } } const generator = myGenerator() console.log(generator.next().value) console.log(generator.next().value) console.log(generator.next().value)
组合使用生成器
function *oneGenerator() { yield 1 yield* twoGenerator() yield 1 } function *twoGenerator() { yield 2 } const generator = oneGenerator() console.log(generator.next().value) // 运行generator的第一个yield. console.log(generator.next().value) // 现在执行twoGenerator! console.log(generator.next().value) // 返回到oneGenerator
Generator辅助异步的执行
通过组合Generator和Promise执行异步操作
Promise.coroutine(function* () { yield Promise.delay(500) console.log('after first delay') yield Promise.delay(1000) console.log('延迟几秒后') })()
Promise.coroutine由bluebired提供,类似库有tj的co
(Generator + Promises) vs Promises
Promise.coroutine(function* () { try { const resultA = yield Promise.resolve('foobar') const resultB = yield Promise.resolve('baz') const resultC = yield Promise.resolve('super') console.log('GENERATORS: ' + resultA + resultB + resultC) } catch (error) { console.log(error) } })()
Promise.resolve('foobar') .then(resultA => { return Promise.resolve('baz') .then(resultB => { return Promise.resolve('super') .then(resultC => console.log('NESTED: ' + resultA + resultB + resultC)) }) }) .catch(error => console.log(error))
Generator与Promise并行异步任务
Promise.coroutine(function *() { const results = yield Promise.all([ Promise.resolve(1), Promise.resolve(2) ]) console.log('array', results[0], results[1]) const [a, b] = yield Promise.all([ Promise.resolve(1), Promise.resolve(2) ]) console.log('destructured', a, b) })()
生成器默认支持Node.js版本4+
async/await
从Generator到async/awaitPromise.coroutine(function* () { const [a, b] = yield Promise.all([ Promise.resolve(1), Promise.resolve(2) ]) console.log(a, b) })()
async function () { const [a, b] = await Promise.all([ Promise.resolve(1), Promise.resolve(2) ]) console.log(a, b) }
调试异步JavaScript
为作为参数的函数命名
function doMyThing() { setTimeout(() => { console.log('NOT NAMED: ' + new Error().stack) }, 50) } doMyThing()
正确的方式
function doMyThing() { setTimeout(function fooIsMyName() { console.log(' NAMED: ' + new Error().stack) }, 50) } doMyThing()
chrome中开启async追踪
async
##Thank
Joseph Andaverde
相关文章推荐
- 深入理解JavaScript系列(3) 全面解析Module模式
- 深入理解JavaScript系列(29):设计模式之装饰者模式
- 深入理解JavaScript系列(28):设计模式之工厂模式
- 深入理解JavaScript系列(36):设计模式之中介者模式
- 深入理解JavaScript系列(32):设计模式之观察者模式
- 深入理解JavaScript系列(25):设计模式之单例模式
- 深入理解JavaScript系列(36):设计模式之中介者模式
- 深入理解JavaScript系列(29):设计模式之装饰者模式
- 深入理解JavaScript系列(25):设计模式之单例模式
- 深入理解JavaScript系列(35):设计模式之迭代器模式
- 深入理解JavaScript系列(27):设计模式之建造者模式
- 深入理解JavaScript系列(34):设计模式之命令模式
- 深入理解JavaScript系列(30):设计模式之外观模式
- 深入理解JavaScript系列(30):设计模式之外观模式
- 深入理解JavaScript系列(3):全面解析Module模式
- 深入理解JavaScript系列(28):设计模式之工厂模式
- 深入理解JavaScript系列(32):设计模式之观察者模式
- 深入理解JavaScript系列(33):设计模式之策略模式
- 深入理解JavaScript系列(33):设计模式之策略模式
- 深入理解JavaScript系列(31):设计模式之代理模式