Open uniquejava opened 6 years ago
6 Reasons Why JavaScript’s Async/Await Blows Promises Away (Tutorial)
How do I convert an existing callback API to promises?
在node.js v8中加了util.promisify
函数.
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
readFile('./notes.txt')
.then(txt => console.log(txt))
.catch(...);
In Node.js 8 you can promisify object methods on the fly using this npm module:
https://www.npmjs.com/package/doasync
It uses util.promisify and Proxies so that your objects stay unchanged. Memoization is also done with the use of WeakMaps). Here are some examples:
With objects:
const doAsync = require('doasync');
doAsync(fs).readFile('package.json', 'utf8')
With functions:
doAsync(request)('http://www.google.com')
You can even use native call
and apply
to bind some context:
doAsync(myFunc).apply(context, params)
.then(result => { /*...*/ });
var getStuffAsync = Promise.promisify(getStuff); // Bluebird
var getStuffAsync = Promise.promisifyAll(API); // Bluebird
var getStuffAsync = Q.denodeify(getStuff); // Q
var getStuffAsync = util.promisify(getStuff); // Native promises, node only
实战
async function add(a, b) {
return a + b;
}
add(1, 2).then(value => console.log(value));
let sum1 = add(1,2)
console.log('sum1=', sum1); // 这里的sum1不是3, 而是Promise(3)
let sum2 = await add(1, 2); // 错误, 这里不能用await
console.log('sum2=', sum2);
总结:
await
.Using async/await with a forEach loop
有问题的代码
import fs from 'fs-promise'
async function printFiles () {
const files = await getFilePaths() // Assume this works fine
files.forEach(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
})
}
printFiles()
解决方法:
Sure the code does work, but I'm pretty sure it doesn't do what you expect it to do. It just fires off multiple asynchronous calls, but the printFiles
function does immediately return after that.
If you want to read the files in sequence, you cannot use forEach
indeed. Just use a modern for … of
loop instead, in which await
will work as expected:
async function printFiles () {
const files = await getFilePaths();
for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}
If you want to read the files in parallel, you cannot use forEach
indeed. Each of the async
callback function calls does return a promise, but you're throwing them away instead of awaiting them. Just use map
instead, and you can await the array of promises that you'll get with Promise.all
:
async function printFiles () {
const files = await getFilePaths();
await Promise.all(files.map(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
}));
}
async/await本来就是基于promise, 它不是要替代promise, 而是要替代callback.
写得zei好, 转自: Working with JavaScript callback APIs from async/await
To ignore Node.js as a possibility in certain problem domains, for which it is the best tool for the job, is a tremendously silly and at times unprofessional decision. While I don't delight in writing JavaScript, I must acknowledge that JavaScript has matured quite nicely over the past ten years. Perhaps the most helpful addition, for me at least, are the
async
andawait
keywords which aim to prevent the callback nightmare many casual JavaScript developers may dread.Particularly for Node applications, callbacks provided a mechanism through which highly event-driven code could be executed. Inside the runtime, this generally means the execution thread can defer certain slow operations, such as timers or network I/O, until the timer fires or the socket's buffer has data available for the application. All the while, executing other "work" within the application. I was first introduced to this cooperative multitasking approach over a decade ago, via "greenlets" in Python, the tools and libraries I used were hacks on top of CPython, and never caught any significant adoption. Node, however, is "just JavaScript" which practically every web application must maintain some familiarity with anyways. This allowed Node to enter a niche, which
Go
would later intrude upon, of lightweight and high-connection-count services.Unfortunately, callback-oriented code is fairly difficult to read and understand as it's execution-flow cannot be read linearly by scrolling down in the text editor. For this reason, in my opinion, the async and await syntax sugar is so valuable in JavaScript. Borrowing from javascriptasyncfunction.com, callback-oriented code such as:
Can be re-written as:
This is all well and good, but only works because the APIs underneath, e.g.
fetch
, have been introduced to support it. For the unfortunate developer (read: me) who must work with the legacy "callback-oriented" APIs, it might not be obvious how to use async and await in an application which must integrate with callback-driven libraries.While banging my head against this problem I learned that JavaScript engines introduced the Promise API, which was somehow related, but it was never succinctly clear how.
What I found so terribly confusing was: I had always seen the
async
andawait
keywords used together but never with a callback-oriented API.It helps to tease the two apart, and explain them separately:
async: should be used with a function declaration to denote that it can be deferred and will, in effect, implicitly return a Promise.
await: should be used to block a sequential flow of execution until a
Promise
can be resolved.await
cannot be used unless the function containing it is markedasync
.Let's say I want to take a function, which currently uses callbacks, and incorporate it into the rest of my
async/await
application. The trick, it turns out, is to wrap it with aPromise
:This
sendMessage
function can then be used in otherasync
type functions, e.g.:This doesn't completely change the writing of JavaScript to a sequential model, the top-level invocation of this function must treat it as a
Promise
, e.g.:notifyBroker().then(() => { /* callback when notifyBroker() completes */ });
It does, however, make it a lot easier to author non-blocking code without a descent into callback hell.