async/await
提供了一种使用同步样式代码异步访问资源的选项,而不会阻塞主线程。然而,使用它有点棘手。在本文中,我们将从不同的角度探讨async / await,并将展示如何正确有效地使用它们。
async / await的好处
async/await
给我们带来的最重要的好处是同步编程风格。我们来看一个例子吧。
// async / await async getBooksByAuthorWithAwait(authorId){ const books = await bookModel.fetchAll(); return books.filter(b => b.authorId === authorId); } // promise getBooksByAuthorWithPromise(authorId){ return bookModel.fetchAll()。then( books => books.filter(b => b.authorId === authorId)); }
很明显,async/await
版本比承诺版本更容易理解。
另一个不太明显的好处是async
关键字。它声明getBooksByAuthorWithAwait()
函数返回值保证是一个promise,以便调用者可以调用getBooksByAuthorWithAwait().then(...)
或await getBooksByAuthorWithAwait()
安全。想想这个例子(不好的做法!):
getBooksByAuthorWithPromise(authorId){ if(!authorId){ return null; } return bookModel.fetchAll()。then( books => books.filter(b => b.authorId === authorId)); }
在上面的代码中,getBooksByAuthorWithPromise
可以返回一个promise(正常情况)或一个null
值(例外情况),在这种情况下,调用者不能.then()
安全地调用 。而通过async
声明,就可以安全的用.then()调用了。
Async/await可能会产生误导
有些文章将async / await与Promise进行比较,并声称它是JavaScript异步编程演变的下一代,我不同意。Async / await是一种改进,但它只不过是一种语法糖,它不会完全改变我们的编程风格。
从本质上讲,异步函数仍然是承诺。在正确使用异步函数之前,您必须了解promises,更糟糕的是,大多数情况下您需要使用promises和异步函数。
考虑上面示例中的getBooksByAuthorWithAwait()
和getBooksByAuthorWithPromises()
函数。请注意,它们不仅在功能上相同,而且具有完全相同的界面!
getBooksByAuthorWithAwait()
如果直接调用,这意味着将返回一个承诺。
嗯,这不一定是坏事。只有这个名字await
让人感觉“哦,这可以将异步函数转换为同步函数”,这实际上是错误的。
Async/await陷阱
那么使用async/await时会出现什么错误?这是一些常见的。
太顺序了
虽然await
可以使您的代码看起来像同步,但请记住它们仍然是异步的,必须注意避免过于顺序。
async getBooksAndAuthor(authorId){ const books = await bookModel.fetchAll(); const author = await authorModel.fetch(authorId); return { author, books:books.filter(book => book.authorId === authorId), }; }
此代码看起来逻辑正确。但这是错误的。
await bookModel.fetchAll()
将等到fetchAll()
返回。- 然后
await authorModel.fetch(authorId)
将被执行。
请注意,authorModel.fetch(authorId)
它不依赖于bookModel.fetchAll()的结果,实际上它们可以并行调用!但是,在这里使用了await,这两个调用变为顺序,并且总执行时间将比并行版本长得多。
这是正确的方法:
async getBooksAndAuthor(authorId){ const bookPromise = bookModel.fetchAll(); const authorPromise = authorModel.fetch(authorId); const book = await bookPromise; const author = await authorPromise; return { author, books:books.filter(book => book.authorId === authorId), }; }
或者更糟糕的是,如果你想逐个获取一个项目列表,你必须依赖Promise:
async getAuthors(authorIds){ // 错误,这将导致顺序调用 // const authors = _.map( // authorIds, // id => await authorModel.fetch(id)); // 正确 const promises = _.map(authorIds,id => authorModel.fetch(id)); const authors = await Promise.all(promises); }
简而言之,您仍然需要异步考虑工作流,然后尝试await同步编写代码。在复杂的工作流程中,直接使用promises可能更容易。