Practical Node.js (2018版) 14章, async code in Node
Asynchronous Code in Node
历史上,Node开发者只能用回调和事件emitters。
现在可以使用一些异步的语法:
- async module
- Promises
- Async/await funcitons
Promise
在hook下,一个基本的promise的运行。
Promises不是取代callback,它仍然使用callback。
例子:
这是传统的callback用法
function myAsyncTimeoutFn(data, callback) { setTimeout(() => { callback() }, 1000) } myAsyncTimeoutFn('just a silly string argument', () => { console.log('Final callback is here') })
Promise改变了代码的布局:
这是模仿Promise原理的代码。返回一个Promise对象。
function myAsyncTimeoutFn(data) { let _callback = null setTimeout( () => { if ( _callback ) _callback() }, 1000) return { then(cb) { _callback = cb } } } myAsyncTimeoutFn('just a silly string argument').then(() => { console.log('Final callback is here') })
这个对象有内有一个特别的方法then。
执行这个方法then,会设置回调_callback变量。
最后调用event queue的任务setTimeout()。执行_callback()。
⚠️:_callback这种写法只是告诉其他开发者这个方法是私有的。
如何处理错误?很简单在返回对象内的then方法添加一个错误参数。
// 使用file system模块中的readFile()方法,异步函数。 const fs = require('fs') function readFilePromise( filename) { let _callback = () => {} let _errorCallback = () => {} fs.readFile(filename, (error, buffer) => { if (error) _errorCallback(error) else _callback(buffer) }) return { then(cb, errCb) { _callback = cb _errorCallback = errCb } } } readFilePromise('package.json').then( buffer => { console.log(buffer.toString() ) process.exit(0) }, err => { console.error(err) process.exit(1) })
语法
p.then(onFulfilled[, onRejected]);
p.then((value) => {
// fulfillment
}, (reason) => {
// rejection
});
1. 执行readFilePromise函数
2. fs.readFile()读数据,并存入内存,等待后续处理。
3. return返回一个对象。
4.调用对象的then方法。
5.从event queue中读取回调函数
(error, buffer) => {
if (error) _errorCallback(error)
else _callback(buffer)
})
改变一下上面的代码:
readFilePromise('package.jsqn').then( buffer => {
就会在terminal 上打印错误信息:
{ [Error: ENOENT: no such file or directory, open 'package.jsqn'] errno: -2, code: 'ENOENT', syscall: 'open', path: 'package.jsqn' }
总结:
我们没有使用回调callback参数在主函数中传递值,相反,我们使用callback参数在then方法内。
callback参数的值是一个函数会在之后处理。就像标准的回调函数一样。
Async functions
一个promise的wrapper。
优势:async/await函数的语法在其他语言如C#内已经存在。
下面使用async/await来重写👆的代码。
async关键词写在关键词function和()=>前面。然后在这个函数内使用await。
await它会暂停当前的函数来等待来自promise或async function的结果
const axios = require('axios') const getAzatsWebsite = async () => { console.log("1") const response = await axios.get('http://azat.co') console.log('wait: 3') return response.data } getAzatsWebsite().then(console.log) console.log('2')
结果:
1 2 wait: 3 <!DOCTYPE html lang="en"> <html> <head> 。。。略
等同:
const axios = require('axios') function getAzatsWebsite() { console.log('1') return axios.get('http://azat.co') .then((response) => { return response.data}) } getAzatsWebsite().then(console.log) console.log("2")
结果:
1
2
<!DOCTYPE html lang="en">
<html>
<head>
。。。略
从上面看到,async函数和promises是兼容的compatible。开发者可以使用then来resolve async functions。
区别是,开发者在async函数内无需在创建一个then声明或者嵌套的callbacks。
看看async/await是如何测试的:
Node Testing:https://github.com/azat-co/node-testing/tree/master/code/rest-test/test
async/await如何用在框架/库,例子Koa
The gist is that async functions are more awesome when you don't resolve them yourself but use them in a framework or a library.
const Koa = require('koa') const app = new Koa() app.use(async ctx => { const response = await axios.get('http://azat.co') ctx.body = response.data }) app.listen(3000)
try/catch不能处理异步的错误。所以promise不使用try/catch。
但是, 使用async/await就可以用try/catch。
const axios = require('axios') const getAzatsWebsite = async () => { try { const response = await axios.get('https://azat.co') return response.data } catch(e) { console.log('oooops') } } getAzatsWebsite().then(console.log) //输出ooops,因为azat.co是http不是https.
async/await也可以throw 错误:
const makeRequest = async () => { const data = await fetchData() const data2 = await processData(data) const data3 = await processData(data2) const data4 = await processData(data3) const data5 = await processData(data4) throw new Error("oops") } makeRequest() .catch(err => { console.log(err) // outputs Error: oops at makeRequest })
For more on async/await vs promise, see this post: https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9.