您的位置:首页 > Web前端 > JavaScript

深入理解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/await

Promise.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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Node.js 异步 模式