关于async函数和普通函数的疑问
发布于 4 年前 作者 varHarrie 3573 次浏览 来自 问答

根据我的了解async函数就是generator的封装,其本质是一个自执行的generator,并且执行返回一个promise。

co的yield/generator在运用上有一个致命的问题,所有用到yield的环境,必须用一个generator包裹,并且父层也必须使用这种方式,顶层调用时,需要使用co去封装,使其自执行。

那么async/await与yield/generator相比有什么优势吗?顶层调用一个async函数,可以当做普通函数调用?

最近在尝试整合koa v2和socket.io,koa路由就是用async/await方式,调用异步函数完成数据库查询,但是在socket.io上的请求,能不能使用async函数替换普通函数呢,如下: socket.io官方文档的写法:

io.on('connect', (socket) => {
  socket.on('join', (roomId) => {  // 这是一个普通函数
    // 如果这里用到异步调用,或者调用promise,就需要使用callback或then方式
	Room.findById(roomId).then((room) => {
		console.log(room)
		// 如果还有多次异步操作就会导致嵌套层数太深
		...
		socket.emit('success', room)
	})
  }
})

异步函数写法(这样写可行吗?)

io.on('connect', (socket) => {
  socket.on('join', async (roomId) => {  // 这是一个async函数
    const room = await Room.findById(roomId).exec()
	...
	// 可能还有其他await异步调用
	socket.emit('success', room)
  }
})

写了一个测试,发现确实能够获得a和b两个promise的结果,那说明async函数可以不用await去调用吗?这样写会不会有什么问题?

const p = function (word, t) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(word + ' ' + t)
      }, t)
    })
  }

  socket.on('say', async () => {
    const a = await p('aaa', 1000)
    const b = await p('bbb', 3000)
    console.log(a, b)
    socket.emit('response', {a, b})
  })
9 回复

目前v8还没有实现async/await(还有不少bug在调(link)) 将async转换成generator只不过是babel根据现有的能力转换而已

https://chromium.googlesource.com/v8/v8.git/+/d08c0304c5779223d6c468373af4815ec3ccdb84 这是5月份实现async原型的提交,粗略的看了一眼,内部是Promise

下面是目前的状态 Async/Await implementation in V8

The status is that this is still pending. Given the dependency here on Promises and the current state of native Promises in core, there are still many issues that have yet to be ironed out. I believe it’s moving in a positive direction but it’s going to take a while longer.

异步函数写法(这样写可行吗?)

不行的, 你在一个异步函数里面 throw 一个 error 试试, 只会导致返回的Promise 值被 reject, 采用Native Promise 实现的话, unhandle rejection 会悄无声息的被吞…

1 co里面可以yield promise和thunk 2

io.on('connect', (socket) => {
  socket.on('join', roomId => (async (roomId) => {  // 这是一个async函数
    const room = await Room.findById(roomId).lean().exec()
	})(roomId)
	.then(room => socket.emit('success', room))
	.catch(err => socket.emit('error', err))
  }
})

https://github.com/socketio/socket.io/issues/2638 这是老外的回复,他说可以,并且使用try/catch去处理异常

@magicdawn 所说的throw一个error,使promise被reject不就是想要的结果吗,想要处理unhandle rejection使用try/catch去处理才比较合理吧

@joesonw co确实是一种解决方案,但是已经上koa v2了,就不想再回头了 如果将async函数当做promise去用then和catch执行,那不还是异步的写法?无限嵌套…

既然已经用koa v2了, 希望能够有统一的一种写法(async/await)

@SinalVee 看来还要再等一段时间啊

@varHarrie socket.io 是事件的, 要自己包装下才可以.

@magicdawn 你说的是对的,这种unhandle rejection在async function之外连try/catch都无法捕获,被吞了一点提示都没有,改用bluebird之后才有异常提示,但是函数外部依然无法捕获。

@varHarrie

const wrap = fn => (() => fn().catch(e => console.error(e.stack || e)));

io.on('connect', (socket) => {
  socket.on('join', wrap(async (roomId) => {  // 这是一个async函数
    const room = await Room.findById(roomId).exec()
	...
	// 可能还有其他await异步调用
	socket.emit('success', room)
  }))
})
回到顶部