我刚刚开始进行react本机开发,遇到了异步函数。有人能用外行的话解释一两件事吗。我试着读过关于它的文章,但他们都倾向于用一种非常技术性的方式来解释它,这有点令人困惑。我使用过其他语言,但javaScript不是我喜欢的。
我的疑虑是:
- 哪一个函数的行为更像正常函数,同步函数还是异步函数
- 在阅读本文时https://blog.expo.io/react-native-meets-async-functions-3e6f81111173他谈到了回报承诺和等待回应。那么,在这个过程中,什么是承诺,如果我们正在等待响应,它不应该是一个同步函数吗
- 当然,同步和异步功能之间的区别
Javascript是一种单线程语言,这意味着处理I/O、套接字和网络等事务的函数在执行时通常会阻塞主线程。为了能够编写并发代码,而不会用可能较慢的任务阻塞主线程,JS使用了所谓的事件循环。因此,异步函数只是一个可以放入队列中的函数,稍后可以检查函数的结果,而不会阻塞主线程。
您可以在MDN上了解更多关于事件循环的信息,并观看Philip Roberts 的演讲
当来自其他编程语言时,这是一个具有挑战性的主题。使用您的术语,"正常"函数类似于同步函数。
我推荐MDN文档等待。阅读该页面,然后运行f1
示例——我在下面包含了一些增强功能:
- 我在console.log中添加了时间戳,这样计时就更明显了
- 我在调用
f1()
之前和之后立即添加了console.log
语句
await
关键字在异步函数内部并不意味着等待(或阻塞(。它分割执行流,暂停f1
函数(大约2秒后将恢复(,并返回一个Promise,允许异步函数的调用方选择是否等待异步函数的结果。在下面的代码中,我们打印出对f1()
的调用结果,但我们选择不等待延迟的结果,而是继续到下一个console.log
。
在Node.js:中运行此代码
///////////////////////////////////////////////////////////////////////
// This is just setting up timestamps for console.log
///////////////////////////////////////////////////////////////////////
const oldlog = console.log;
console.log = function () {
var args = [].slice.call(arguments);
oldlog.apply(console.log,[getTimestamp()].concat(args));
};
const getTimestamp = () => '[' + (new Date()).toISOString() + ']';
///////////////////////////////////////////////////////////////////////
// Real code starts here
///////////////////////////////////////////////////////////////////////
function resolveAfter2Seconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
console.log('enter f1');
const x = await resolveAfter2Seconds(10);
console.log('exit f1, x =', x);
return x;
}
console.log('before f1');
const y = f1();
console.log('after f1, y =', y);
运行时,这将导致如下结果:
[2020-03-03T01:48:50.716Z] before f1
[2020-03-03T01:48:50.719Z] enter f1
[2020-03-03T01:48:50.720Z] after f1, y = Promise { <pending> }
[2020-03-03T01:48:52.725Z] exit f1, x = 10
请特别注意,在我们看到exit f1
日志之前,我们看到了after f1
日志。执行流被拆分,f1()
被暂停,而f1()
的调用方继续。f1()
的执行在大约2秒后恢复。
现在,将其与调用f1()
的结果await
进行比较。请注意,因为我们现在使用await
,所以必须将代码包装在async
(实际上是一个异步IIFE(中,因为await
只能在async
函数内部使用。
// console.log('before f1');
// const y = f1();
// console.log('after f1, y =', y);
(async () => {
console.log('before f1');
const y = await f1();
console.log('after f1, y =', y);
})();
现在,输出如下:
[2020-03-03T02:19:18.122Z] before f1
[2020-03-03T02:19:18.124Z] enter f1
[2020-03-03T02:19:20.130Z] exit f1, x = 10
[2020-03-03T02:19:20.130Z] after f1, y = 10
请注意,现在,由于调用者选择等待调用f1()
的结果,我们看到after f1
和exit f1
日志颠倒了(使用您的术语,按"正常"顺序(。现在f1()
的结果是10,而不是一个待定的Promise。
因此,这是一个有点棘手的问题,我鼓励更多的阅读和实验来掌握它。它看起来很复杂,但实际上现在编写异步JavaScript代码比在该语言中引入async/await之前更简单。