Practical Node.js (2018版) 14章, async code in Node

Asynchronous Code in Node


 

历史上,Node开发者只能用回调和事件emitters。

现在可以使用一些异步的语法:

  • async module
  • Promises
  • Async/await funcitons

 


 

Promise

the docs,

在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.

 

posted @ 2019-02-04 10:04  Mr-chen  阅读(152)  评论(0编辑  收藏  举报