几乎所有地方都可以使用 async/await 吗?
Is it OK to use async/await almost everywhere?
我目前正在编写供个人使用的小型 NodeJS CLI 工具,我决定尝试使用 Babel 的 ES7 async/await 功能。
它是一个网络工具所以我显然有异步网络请求。我为 request
包写了一个简单的包装器:
export default function(options) {
return new Promise(function(resolve, reject) {
request({...options,
followAllRedirects: true,
headers: {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0"
}
}, (error, response, body) => {
if(error) {
return reject(error);
}
resolve({response: response, body: body});
});
});
}
现在我可以做类似的事情了
async function getGooglePage() {
try {
var r = await request({url: "http://google.com"});
console.log(r.body);
console.log("This will be printed in the end.")
} catch(e) {
console.log(e);
}
}
getGooglePage();
现在我有一个问题:我在很多地方做请求,我必须将所有这些功能标记为 async
,这是一个好的做法吗?我的意思是我代码中的几乎每个函数都应该是 async
,因为我需要 await
来自其他 async
函数的结果。这就是为什么我认为我误解了 async/await 概念。
async/await 有时称为 "contagious" 或 "viral"(或者在 C# 世界中如此),因为为了使其有效,它需要得到所有支持沿着调用链的方式。强制某些异步行为同步可能会导致意想不到的结果,因此您应该将其从原始方法一直扩展到使用它的顶级消费者。换句话说,如果您创建或使用一个使用它的类型,该类型也应该实现它,依此类推。所以是的,您应该将异步添加到本身依赖它的每个函数。但是请注意,您不应该先发制人地向实际上没有实现或不需要它的函数添加异步。
想一想:如果您使用 async
(我的意思是 await
某种东西),您就是 async
。避免将 async
调用压缩为同步调用。
I do requests in many places and I have to mark all these functions as async
是的,如果您的所有代码都是异步的,那么您将在任何地方使用 async
函数。
让所有代码都是异步的会使事情变得复杂。你必须担心无处不在的竞争条件,确保正确处理可重入函数,并记住在每个 await
期间基本上任何事情都可能发生。
I mean that almost every function in my code should be async because I need to await a result from other async functions.
这可能不是最佳做法。您可以尝试将代码分解成更小的单元,其中大部分通常不是异步的。所以不要写
async function getXandThenDoY(xargs) {
let res = await get(xargs);
…
return …;
}
你应该考虑做两个函数
function doY(res) {
// synchronous
…
return …;
}
function getXandDoY(xargs) {
// asynchronous
return get(xargs).then(doY);
}
/* or, if you prefer:
async function getXandDoY(xargs) {
return doY(await get(xargs));
}
*/
我有 the same question.
并发现 a great answer here →“陷阱 3:你的整个堆栈需要异步”
不,async/await
没有传染性。(我也相信了一段时间)
您始终可以将同步函数的结果视为承诺,然后您就恢复正常了。
来自 developer.mozilla.org:
The async function declaration defines an asynchronous function…
Return value:
A Promise which will be resolved with the value
returned by the async function, or rejected with an uncaught exception
thrown from within the async function.
示例代码:
const log = console.log; // just lazy shorthand
// just to delay, as seen in many places
function promiseTimeout(time, value) {
return new Promise(function(resolve, reject) {
setTimeout(function() { resolve(value); }, time);
});
};
// the thing you care about
async function foo() {
Promise.resolve('here')
.then((a) => {log('doing stuff '+a); return 'pear'})
.then(function(v) {
return promiseTimeout(1000,v)
});
};
// treat async-function like promise:
foo().then(function(){ log('folling up in main, ')});
// log('bad end');
让你:
doing stuff here
following up in main
启用 'bad end' 会出现得太早。你只能等待你使用等待的东西。 (如果你这样做了,请记住:这只是语法糖,让你不必将后续代码塞进 .then()
子句中......很好,但仅此而已。)
我目前正在编写供个人使用的小型 NodeJS CLI 工具,我决定尝试使用 Babel 的 ES7 async/await 功能。
它是一个网络工具所以我显然有异步网络请求。我为 request
包写了一个简单的包装器:
export default function(options) {
return new Promise(function(resolve, reject) {
request({...options,
followAllRedirects: true,
headers: {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0"
}
}, (error, response, body) => {
if(error) {
return reject(error);
}
resolve({response: response, body: body});
});
});
}
现在我可以做类似的事情了
async function getGooglePage() {
try {
var r = await request({url: "http://google.com"});
console.log(r.body);
console.log("This will be printed in the end.")
} catch(e) {
console.log(e);
}
}
getGooglePage();
现在我有一个问题:我在很多地方做请求,我必须将所有这些功能标记为 async
,这是一个好的做法吗?我的意思是我代码中的几乎每个函数都应该是 async
,因为我需要 await
来自其他 async
函数的结果。这就是为什么我认为我误解了 async/await 概念。
async/await 有时称为 "contagious" 或 "viral"(或者在 C# 世界中如此),因为为了使其有效,它需要得到所有支持沿着调用链的方式。强制某些异步行为同步可能会导致意想不到的结果,因此您应该将其从原始方法一直扩展到使用它的顶级消费者。换句话说,如果您创建或使用一个使用它的类型,该类型也应该实现它,依此类推。所以是的,您应该将异步添加到本身依赖它的每个函数。但是请注意,您不应该先发制人地向实际上没有实现或不需要它的函数添加异步。
想一想:如果您使用 async
(我的意思是 await
某种东西),您就是 async
。避免将 async
调用压缩为同步调用。
I do requests in many places and I have to mark all these functions as async
是的,如果您的所有代码都是异步的,那么您将在任何地方使用 async
函数。
让所有代码都是异步的会使事情变得复杂。你必须担心无处不在的竞争条件,确保正确处理可重入函数,并记住在每个 await
期间基本上任何事情都可能发生。
I mean that almost every function in my code should be async because I need to await a result from other async functions.
这可能不是最佳做法。您可以尝试将代码分解成更小的单元,其中大部分通常不是异步的。所以不要写
async function getXandThenDoY(xargs) {
let res = await get(xargs);
…
return …;
}
你应该考虑做两个函数
function doY(res) {
// synchronous
…
return …;
}
function getXandDoY(xargs) {
// asynchronous
return get(xargs).then(doY);
}
/* or, if you prefer:
async function getXandDoY(xargs) {
return doY(await get(xargs));
}
*/
我有 the same question.
并发现 a great answer here →“陷阱 3:你的整个堆栈需要异步”
不,async/await
没有传染性。(我也相信了一段时间)
您始终可以将同步函数的结果视为承诺,然后您就恢复正常了。
来自 developer.mozilla.org:
The async function declaration defines an asynchronous function…
Return value: A Promise which will be resolved with the value returned by the async function, or rejected with an uncaught exception thrown from within the async function.
示例代码:
const log = console.log; // just lazy shorthand
// just to delay, as seen in many places
function promiseTimeout(time, value) {
return new Promise(function(resolve, reject) {
setTimeout(function() { resolve(value); }, time);
});
};
// the thing you care about
async function foo() {
Promise.resolve('here')
.then((a) => {log('doing stuff '+a); return 'pear'})
.then(function(v) {
return promiseTimeout(1000,v)
});
};
// treat async-function like promise:
foo().then(function(){ log('folling up in main, ')});
// log('bad end');
让你:
doing stuff here
following up in main
启用 'bad end' 会出现得太早。你只能等待你使用等待的东西。 (如果你这样做了,请记住:这只是语法糖,让你不必将后续代码塞进 .then()
子句中......很好,但仅此而已。)