node 初步 (三)

上一节中, 我们为了优化异步的书写, 写出了生成器函数和promise的结合版本, 这看起来已经好极了
唯一的遗憾就是在需要每次书写 run 函数 并在 run 函数中构建一个生成器函数
实际上, 浏览器已经为此设计了一个专门的语法, 我们不再需要自己构建一个 run 函数了, 因为在浏览器内部这个函数已经被实现好了, 格式如下:

function squareAsync(x) {
  return new Promise(resolve => {
    setTimeout(() => resolve(x * x), 1000 + Math.random() * 2000)
  })
}

async function foo() {  // 这句对应 function * foo()
  var a = await squareAsync(5) // await 对应 yield
  console.log(a)
  return Promise.reject(3)
}
// > Promise {<pending>}
// 25
// 3

foo().catch(val => console.log(val))

这里的async function foo() 就像浏览器在run中运行 foo, 此时也更方便给 foo 函数传参, 同样 await 后接 promise, 函数运行中遇到 await 就会暂停, 直到等到 promise 的运行结果再继续执行, 这里的 promise 不再需要调用 then 来得到求值结果, 直接就相当于一条赋值语句。 async 并不是一个关键字, 必须和function一起才有效果
注意,这里函数的返回结果是一个promise, 所以可以在 foo 后调用 then/catch 来接收 foo 的返回结果

举几个例子

在前面学习 Promise 时, 我们举过加载图片的例子

  • 串行加载, 串行显示
async function showStory(storyUrl) {
  var story = await getJSON(storyUrl)
  for (var chapterUrl of story.chapterUrls) {
    var chapter = await getJSON(chapterUrl)//一张一张加载
    addContentToPage(chapter)
  }
}
  • 并行加载, 串行显示
async function showStory(storyUrl) {
  var story = await getJSON(storyUrl)
  var chapterPromises = story.chapterUrls.map(getJSON)// 同时加载
  for (var chapterPromise of chapterPromises) {
    var chapter = await chapterPromise
    addContentToPage(chapter)
  }
}

再来写一下上一节读取文件名的第 4 个版本, async版本的函数

const fs = require('fs')
const fsp = fs.promises
async function listAllFiles(path) {
  var result = []
  var stat = await fsp.stat(path)
  if (stat.isFile()) {
    return [path]
  } else {
    var entries = await fsp.readdir(path, { withFileTypes: true })
    for (let entry of entries) { // 这里不能用forEach. 因为forEach不能接异步函数
      var fullPath = path + '/' + entry.name
      var files = await listAllFiles(fullPath) // ①
      result.push(...files)
    }
    return result
  }
}
listAllFiles('.').then(console.log)

分析一下, 如果这么写的话, 效率可能并没有原来高了, 因为在 ① 处第一个文件夹加载完了之后才能加载第二个, 此时我们可以把它优化为并行加载/串行显示的方式:

const fs = require('fs')
const fsp = fs.promises
async function listAllFiles(path) {
  var result = []
  var stat = await fsp.stat(path)
  if (stat.isFile()) {
    return [path]
  } else {
    var entries = await fsp.readdir(path, { withFileTypes: true })
    var entryPromises = entries.map(entry => {
      var fullPath = path + '/' + entry.name
      return listAllFiles(fullPath)
    })
    for (let entryPromise  of entryPromises ) { 
      var files = await entryPromise
      result.push(...files)
    }
    return result
  }
}
listAllFiles('.').then(console.log)

这种方法才是性能更高的一个, 同时在等待的时候 cpu 还可能运行其他的程序, 但是还是有可以提升的地方

图片描述

就像这样,图中黑色部分是等待时间, 蓝色为执行时间,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章