lbwa / set.sh-stale

✍A place which is used to share my programming experiences in Chinese. 一个分享代码经历的地方。
https://set.sh
0 stars 0 forks source link

async 函数中的并发 #22

Open lbwa opened 6 years ago

lbwa commented 6 years ago

async 函数中的并行执行

async 函数中,仅当存在 await 关键词的地方表示需要等待计算结果。那么存在互不依赖的操作时,不要错失并行执行的机会。

Notice: 以下示例代码中的 wait() 方法表示异步操作。因为在 async 函数中的同步操作与有没有 await 不对整个 async 函数内部执行顺序造成影响。因为函数体中的同步操作将按照常规单线程 event loop 执行,await 后如果是非 Promise 对象,那么 await直接 返回一个包含该同步操作的 Promise 对象。

async function series () {
  // 需要等待 wait(500) 的计算结果才能继续执行函数体
  await wait(500)
  await wait(500)
}

以上代码中,op0op1 的运行关系是同步执行,即只有等到 op0 执行完成后才会执行 op1

async function parallel () {
  // 没有 await 关键词,那么将直接调用 wait(500),执行该异步操作。
  const op0 = wait(500)

  // 因为前一个 op0 是异步操作,且没有 `await`,根据 `ES` 规范中 `event loop` 的描述,
  // `new Promise(...)` 参数函数在本轮 `event loop` 中立即执行,之后该 `promise` 实例将脱离
  // execution context stack 在堆内存中等待异步执行结果。
  // 所以不论 op0 的异步操作有没有执行完成,此时将继续调用一个新的异步操作 wait(500)
  const op1 = wait(500)

  // 此时 op0 与 op1 是并行执行,并且此时在同时等待二者的执行结果
  // 那么整个 parallel 执行时间将缩短
  const result0 = await op0
  const result1 = await op1
}

以上代码 op0op1 将并行执行。

同时还存在以下一种并行执行的写法:

async functon parallel () {
  const [result0, result1] = await Promise.all([wait(500), wait(500)])
}

Reference

lbwa commented 5 years ago

Nodejs 中的异步读取

const jsonPromises = urls.map(async url => {
  const response = await fetch(url);
  return response.json();
})

在以上代码中,值得注意的是数组的 map 方法不在乎传入的参数是异步还是同步的函数。示例代码中的 map 方法仅仅把异步函数当作一个 返回 Promise 对象的函数 来对待。那么它并不会等到异步函数完全执行完成就会继续调用下一个异步函数。

究其原因就是 map 将异步函数看作一个返回 Promise 对象的函数,因为于异步函数体内何时 resolved,外部是无法感知的。map 就仅仅会调用完成异步函数,就表示调用 '完成',那么它就会继续调用下一个异步函数。特别地,异步函数体内的 await 语句总是通过在微任务中调用来实现非阻塞流程。

More samples

// client.js
[1,2,3,4].map(async () => {
  return await fetch('http://localhost:3000').then(res => res.json())
})
// server.js
const http = require('http')

http.createServer(function (req, res) {
  console.log('req.url :', req.url)

  res.writeHead(200, {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*'
  })

  // 五秒后响应
  setTimeout(() => {
    res.end(JSON.stringify({
      msg: Date.now()
    }))
  }, 5000)
}).listen(3000)

client 发起请求时,server 端中返回的响应数据包含响应时间。若客户端中的异步函数是串行请求,那么 client 端接受到的响应数据中各自的时间的间隔至少是 5 秒,而结果显示他们之间几乎是同时收到响应数据,也就表明 server 端是同时接受到 client 端发起的 4 个请求,那么也就表明 client 端的 map 函数仅仅将参数中的异步函数当作一个返回 Promise 对象的普通函数来对待,map 函数不会等到异步函数完全执行就会调用下一个异步函数。

Reference