考虑以下代码:
async function load() {
const data = await new Promise(resolve => {
setTimeout(() => resolve([1, 2, 3]), 10);
}).then(data => data.map(i => i * 10));
console.log(`Data inside the function: ${JSON.stringify(data)}`);
return data;
}
function main() {
const data = load();
console.log(`Loaded data: ${JSON.stringify(data)}`);
}
main();
这是我得到的输出:
Loaded data: {}
Data inside the function: [10,20,30]
但是如果我把代码改成这样:
async function load() { const data = await new Promise(resolve => { setTimeout(() => resolve([1, 2, 3]), 10); }).then(data => data.map(i => i * 10)); console.log(`Data inside the function: ${JSON.stringify(data)}`); return data; } async function main() { const data = await load(); console.log(`Loaded data: ${JSON.stringify(data)}`); } main();
我会得到这个:
Data inside the function: [10,20,30]
Loaded data: [10,20,30]
我很困惑,因为根据文档,wait
应该暂停执行,直到promise得到解决。在这种情况下,第一个示例应该以数组的形式返回数据。但正如你所看到的,它正在返回一个
promise
,我不知道为什么!?
同时,文档中有这一部分,我不明白它在说什么:
等待可以分割执行流,允许等待函数的调用方在等待函数的延迟延续之前恢复执行。在等待延迟其函数的继续后,如果这是该函数执行的第一个等待,则立即执行也通过向函数的调用方返回等待函数完成的未决promise并恢复该调用方的执行来继续。
在我看来,只有当代码中的所有函数都是
async
时,wait
才起作用,这很可笑,因为如果我使用的是另一个模块中的函数,我怎么知道它是不是async
!?或者我应该谨慎一点,总是用await
调用所有函数,不管它们是否是async
!!!
[更新]
感谢大家的参与,并为我提供了见解。但我仍然不知道应该如何使用
await
和async
。我是否应该始终使用wait
调用所有函数?
假设我正在编写一个由多个文件中的多个函数组成的代码。如果我最终使用的库返回
promise
,或者它是异步
函数,那么我是否应该将所有函数调用从异步点追溯到应用程序的入口点,并在进行异步
后的所有函数调用之前添加一个等待
?或者我应该养成用await
调用所有函数的习惯,不管它们是否是async
?
因为第一个函数是异步的,所以它是在执行main
函数的其余部分时运行的,当结果记录到下一行时,这是没有帮助的。在使用结果之前,您必须等待函数完成执行,因此可以使用async
/wait
,如示例所示:
async function main() {
const data = await load();
console.log(`Loaded data: ${JSON.stringify(data)}`);
}
或者使用。然后
:
function main() {
load().then(data => {
console.log(`Loaded data: ${JSON.stringify(data)}`);
});
}
这里的提示是:如果函数是
async
,您必须错误地使用它async
,因为它总是返回一个Promise。
async
/wait
只是语法糖,这意味着,它们不会给语言带来任何新功能,只是有用的promise包装器。
如果函数标记为async
,它总是返回一个promise:
> async function f() { return 42; }
undefined
> f()
Promise { 42 }
此外,如果一个函数是async
,您可以wait
为它里面的任何promise(包括另一个async
函数的结果),并且函数代码的执行将暂停在wait
,直到promise被解决或拒绝。
回答您的问题:如果使用库函数,您通常知道它是否返回promise(如果它标记为async
,它肯定会返回)。因此,请确保等待它或使用
。然后使用返回的promise。
所有async
函数都返回一个promise。都是。
这一promise最终将用您从异步函数返回的任何值来解决。
wait
仅阻止执行内部的async
函数。它不会阻止函数之外的任何内容。从概念上讲,异步函数开始执行,一旦它命中waiit
指令,它立即从函数返回一个未实现的promise,外部执行世界得到这个promise并继续执行。
稍后,wait
ed中的内部promise将得到解决,然后继续执行函数的其余内部。最终,函数的内部将完成并返回一个值。这将触发解析从具有该返回值的函数返回的promise。
仅供参考,在你的load()
函数中有很多多余的东西。你可以从这里改变它:
async function load() {
const data = await new Promise(resolve => {
setTimeout(() => resolve([1, 2, 3]), 10);
}).then(data => data.map(i => i * 10));
console.log(`Data inside the function: ${JSON.stringify(data)}`);
return data;
}
对此:
function load() {
return new Promise(resolve => {
setTimeout(() => resolve([1, 2, 3]), 10);
}).then(data => data.map(i => i * 10));
}
然后,像这样使用它:
load().then(result => {
console.log(result);
});
或者,我更喜欢将promise的手动创建封装在自己的函数中,如下所示:
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
}
function load() {
return delay(10, [1, 2, 3]).then(data => data.map(i => i * 10));
}
事实证明,这个小小的delay()
函数通常在许多需要延迟promise链的地方都很有用。
感谢大家的参与,并为我提供了见解。但我仍然不知道应该如何使用wait和async。
首先,如果需要在函数内部使用wait
,则大多数情况下只标记函数async
。
其次,当您有多个异步操作并且希望对它们进行排序时,您通常使用await
(在async
函数中),这通常是因为第一个操作提供的结果用作第二个操作的输入。当您只有一个异步操作时,可以使用await
,但与简单的.then()
相比,它并没有多大优势。
下面是一些使用async/wait
的好理由:
对多个异步操作进行排序
假设您有所有异步的getFromDatabase()
、getTheUrl()
和getTheContent()
。如果有任何失败,您只需要用第一个错误拒绝返回的promise。
以下是没有异步/等待时的情况:
function run() {
return getFromDatabase(someArg).then(key => {
return getTheURL(key);
}).then(url => {
return getTheContent(url);
}).then(content => {
// some final processing
return finalValue;
});
}
下面是使用async/await
时的情况:
async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
在这两种情况下,函数都返回一个与finalValue解析的promise,以便调用者使用相同的两种实现:
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
但是,您会注意到async/wait
实现更像是序列化的同步外观,看起来更像非异步代码。许多人发现这更容易写,更容易阅读,更容易维护。步骤之间的处理越多,包括分支,async/wait版本的优势就越大。
自动捕获被拒绝的promise和同步异常
正如我前面所说,async
函数总是返回一个promise。他们还必须进行内置的错误处理,自动将错误传播回返回的promise。
不言而喻,如果您手动从async
函数返回一个promise,并且该promise拒绝,那么从async
函数返回的promise将拒绝。
但是,如果您使用的是await
和任何等待拒绝的promise,并且promise上没有.catch()
,并且周围没有try/catch
,则函数返回的promise将自动拒绝。那么,回到我们前面的例子:
async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
如果正在等待的三个promise中的任何一个被拒绝,那么函数将短路(停止在函数中执行任何更多的代码)并拒绝异步返回的promise。所以,您可以免费获得这种形式的错误处理。
最后,一个async
函数也为您捕获同步异常,并将它们转换为拒绝的promise。
在返回promise的正常函数中,如前面所述:
function run() {
return getFromDatabase(someArg).then(key => {
return getTheURL(key);
}).then(url => {
return getTheContent(url);
}).then(content => {
// some final processing
return finalValue;
});
}
如果getFromDatabase()
引发同步异常(可能是因为someArg
无效而触发),则整个函数run()
将同步引发。这意味着,调用方要捕获run()
中所有可能的错误,必须用try/catch
来捕获同步异常,并使用.catch()
来捕获被拒绝的promise:
try {
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
} catch(e) {
console.log(err);
}
这很混乱,而且有点重复。但是,当run()
声明为async
时,它将永远不会同步抛出,因为任何同步异常都会自动转换为被拒绝的promise,因此您可以确保以这种方式写入时捕获了所有可能的错误:
async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
// will catch all possible errors from run()
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
我应该总是用等待来调用所有函数吗?
首先,您只会在返回promise的函数中使用wait
,因为如果函数不返回promise,wait
就没有用了(如果不需要,只会增加代码的混乱)。
其次,您是否使用await
取决于调用函数的上下文(因为您必须使用async
函数才能使用await
),取决于逻辑流以及使用await
是否有益。
没有意义使用的地方等待
async function getKey(someArg) {
let key = await getFromDatabase(someArg);
return key;
}
这里的等待
没有做任何有用的事情。您没有对多个异步操作进行排序,也没有对返回值进行任何处理。您只需直接返回promise即可完成完全相同的代码:
async function getKey(someArg) {
return getFromDatabase(someArg);
}
而且,如果您知道getFromDatabase()
从不同步抛出,您甚至可以从声明中删除async
:
function getKey(someArg) {
return getFromDatabase(someArg);
}
假设我正在编写一个由多个文件中的多个函数组成的代码。如果我最终使用了一个返回promise的库,或者它是一个异步函数,那么我是否应该将所有函数调用从异步点追溯到应用程序的入口点,并在使它们异步之后在所有函数调用之前添加一个wait?
这是一个过于笼统的问题,在一般情况下很难回答。以下是关于这一总体方向的一些想法:
>
一旦您试图从函数A()
返回的结果的任何部分是异步的,或者使用任何异步操作来获得,函数本身就是异步的。在纯Javascript中,您永远不能同步返回异步结果,因此您的函数必须使用异步方法来返回结果(promise、回调、事件等)。
任何函数B()
调用异步函数A()
,并且试图根据从A()
获得的结果返回结果,现在也是异步的,并且必须使用异步机制。对于调用B()
并需要将其结果返回给调用方的函数C()
来说,情况就是如此。所以,你可以说异步行为是有传染性的。在调用链中达到不再需要返回结果的某个点之前,一切都必须使用异步机制来传达结果、错误和完成。
无需特别标记函数async
,除非您特别需要async
函数的一个优点,例如在该函数内使用wait
或它提供的自动错误处理功能。不必在函数声明中使用async
,就可以编写返回promise的函数。所以,“不”我不会返回调用链,使所有的都是异步的。我只在有特定原因的情况下使函数异步。通常原因是我想在函数中使用
wait
,但也有自动捕获同步异常的功能,这些异常会转化为我前面描述的promise拒绝。对于行为良好的代码,通常不需要这样做,但对于行为不良的代码或行为未定义的代码,这样做有时很有用。
wait
也仅在有特定原因时使用。我不只是在每个返回promise的函数上自动使用它。我已经描述了使用它的上述原因。仍然可以使用.then()
处理返回promise的单个函数调用的结果。在某些情况下,使用.then()
或wait
只是个人风格的问题,没有特别的原因。
或者我应该养成习惯,用wait调用所有函数,而不管它们是否异步?
绝对不是!首先,您最不希望做的事情是采用完全同步的代码,不必要地使其异步,甚至使其看起来异步。异步代码(即使是
async
和await
)的编写、调试、理解和维护都比同步代码复杂,因此您永远不会希望通过在其中添加async/await
将同步代码不必要地转换为异步代码:
例如,你永远不会这样做:
async function random(min, max) {
let r = await Math.random();
return Math.floor((r * (max - min)) + min);
}
首先,这是一个完全同步的操作,可以这样编码:
function random(min, max) {
let r = Math.random();
return Math.floor((r * (max - min)) + min);
}
其次,第一个
async
实现使得函数很难使用,因为它现在有一个异步结果:
random(1,10).then(r => {
console.log(r);
});
而不仅仅是简单的同步使用:
console.log(random(1,10));
我有一个异步函数,它调用其中的另一个异步函数。此异步函数正在返回" 你能帮我解决这个问题吗? 以下是第一个异步函数: 如你所见,我调用updateAllProducts函数,并将该值存储到一个变量调用newAllProducts. updateAllProducts是另一个异步函数。 以下是updateAllProducts的代码: 此updateAllProducts函数正在调用另一个异步函数调
问题内容: 我的代码: 当我尝试运行这样的东西时: 我越来越: 但为什么? 我的主要目标是将令牌(从令牌中返回承诺)转换为变量。然后才执行一些操作。 问题答案: 只要其结果尚未解决,promise将始终记录未决。无论promise状态如何(已解决或仍处于待处理状态),您都必须调用promise来捕获结果: 这是为什么? 承诺只是向前的方向;您只能解决一次。a的解析值传递给其或方法。 根据Promi
我试图利用es7异步功能,即。 在这里,所有promise*函数都进行ajax调用,并返回或如果ajax响应满足传递的参数,我相信我不能连续使用3个等待,因此需要一种方法来等待所有这些调用以某种方式返回它们的值。
问题内容: 我的Python程序中有一个保存函数,如下所示: 在此,n为“ 1”。 我收到如下错误: 在外壳中执行相同的加载后,我没有收到任何错误: 为什么会有问题? 问题答案: 您可能从os模块导入了星号: 因此您使用了错误的打开功能。(我想您可以简单地完成,但是可能性较小。)通常,应避免这种导入样式,在实际情况下应避免使用。
我的JavaScript代码如下所示: 完成所有这些异步调用后,我想计算所有数组的最小值。 我怎么能等到他们所有人呢? 我现在唯一的想法是有一个布尔数组叫做done,并在第i个回调函数中将done[i]设置为true,然后说while(not all are done){} edit:我想一个可能的,但很难看的解决方案是在每个回调中编辑done数组,然后在每个回调中设置了所有其他done的情况下调
我正在使用节点8.x。因此,我可以访问所有最新的特性,如异步/等待等。 该场景类似于以下内容(语法不正确,仅供解释): 基本上,User对象的创建依赖于address对象的创建。我希望createUser函数异步执行,即尽快返回一个promise而不等待address对象的创建。 这个问题的目的不是完成任务,而是了解在异步编程中解决这类问题的最佳方法是什么。 我能想到的方法不多:1:创建一个新的p