We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
如何理解async/await?
async/await有哪些使用场景?
对于不支持async/await的低版本babel如何实现的?
async/await与Generator是什么关系?
async/await与promise是什么关系
async/await与Iterator是什么关系
async/await的本质是什么?
async/await 的设计主要是为了简化JavaScript的异步编程和提高代码的可读性和可维护性。
在JavaScript中处理异步操作,早期主要依赖于回调函数(callbacks)和事件监听,但这导致代码很容易陷入“回调地狱”,即回调函数套回调函数,使得代码难以阅读和理解。
后来Promise应运而生,虽然它改善了异步操作的书写方式,避免了多层的回调嵌套,但在某些复杂的异步逻辑处理中依然显得累赘。
而async/await是基于Promise实现的,它通过语言层面提供了更为优雅的异步方案。在语法上,async/await使得异步代码的书写和普通的同步代码几乎没有差别,大大提高了代码可读性,也便于开发者理解代码的执行逻辑。
同时,使用async/await也使得对异步操作的错误处理变得更为方便,可以直接使用try/catch结构进行异常处理,这与同步代码中的错误处理方式一致,从而提高代码的一致性和可维护性。
关于异步方案的发展背景可以参考这篇文章 js三座大山之异步二异步方案
这里需要先掌握好前置知识再来阅读编译后的代码
async function t(){ const res1 = await 1; const res2 = await 2; const res3 = await 3; return res1 + res2 + res3 } t().then(console.log); // 6
将babel目标设置为Safari 10 代码如下:
这里对参数名做了些改动便于理解。
// 定义一个处理异步生成器每一步的函数 function asyncGeneratorStep(iterator, resolve, reject, _next, _throw, next, v) { try { // 尝试从生成器中获取下一个值 var i = iterator[next](v); var u = i.value; } catch (n) { // 如果有错误发生,则使用reject函数抛出错误 return void reject(n); } // 如果生成器完成,则使用resolve函数处理最后的值,否则继续进行完生成器的剩余部分 i.done ? resolve(u) : Promise.resolve(u).then(_next, _throw); } // 定义一个返回生成器函数的函数 function _asyncToGenerator(generator) { return function () { var self = this; var arg = arguments; return new Promise(function (resolve, reject) { var iterator = generator.apply(self, arg); function _next(v) { // 在每个Promise被resolve时,调用_generator的步骤函数 asyncGeneratorStep(iterator, resolve, reject, _next, _throw, 'next', v); } function _throw(v) { // 在每个Promise被reject时,调用_generator的步骤函数 asyncGeneratorStep(iterator, resolve, reject, _next, _throw, 'throw', v); } // 启动生成器 _next(void 0); }); }; } // 定义一个异步函数 function _async() { var _a = _asyncToGenerator(function* () { var res1 = yield 1; var res2 = yield 2; var res3 = yield 3; // 返回这三个值的和 return res1 + res2 + res3; }); // 使用apply方法调用函数,确保this和参数都正确地传递给函数 return _a.apply(this, arguments); } // 封装_async函数的调用 function asyncBabelPolyfill() { return _async.apply(this, arguments); } // 执行函数并在Promise resolve时打印结果 asyncBabelPolyfill().then(console.log); // 输出6
asyncGeneratorStep 函数的作用: asyncGeneratorStep 是一个辅助函数,用于处理生成器函数的每一步执行。它接收以下参数:
asyncGeneratorStep
iterator
resolve
reject
_next
_throw
next
throw
v
当生成器函数执行到 yield 表达式时,它会暂停执行并返回一个对象,该对象包含 value 属性(yield 表达式的结果)和 done 属性(表示生成器是否完成)。asyncGeneratorStep 会根据 done 属性的值来决定是继续执行生成器的下一条 yield 表达式,还是解决 Promise。
yield
value
done
_asyncToGenerator 函数的作用: _asyncToGenerator 是一个高阶函数,它接收一个生成器函数 n 并返回一个新的函数。这个新函数在内部使用 asyncGeneratorStep 来处理生成器的每一步。当调用这个新函数时,它会创建一个 Promise,并在生成器函数中使用 _next 函数来启动生成器。每当生成器函数遇到 yield 表达式时,_next 函数会被调用,并将 yield 的结果作为参数传递给 asyncGeneratorStep。如果生成器函数抛出错误,_throw 函数会被调用来处理错误。
_asyncToGenerator
n
_async 函数的作用: _async 函数是一个使用 _asyncToGenerator 转换的生成器函数。它定义了一个生成器,该生成器在每个 yield 表达式处暂停,并接受一个值,这些值最终被加和并返回。这个函数通过 _asyncToGenerator 转换后,返回了一个promise。
_async
asyncBabelPolyfill: 编译后的入口函数。
babel将async/await转换为Generator函数,并且会自动遍历返回的迭代器,从yield表达式读取输入 并将结果通过next返回给调用方,然后封装为一个promise对象,当迭代器执行完毕,resolve这个promise对象,如果迭代过程中报错则reject这个promise。
所以async/await的本质我们可以理解为:async/await = Generator + 自动遍历Iterator + Promise
使用 void 0 时,实际上是在调用 void 运算符并传递 0 作为参数。由于 void 运算符总是返回 undefined,所以 void 0 的结果也是 undefined。
void 0
void
0
undefined
使用 void 0 而不是直接写 undefined 的一个原因是,undefined可以被重新赋值为其它值,而void 0一直保持是undefined。这样可以防止在那些可以改变undefined值的环境中出错。
undefined = "new value"; // 在老的JavaScript版本中这是允许的 console.log(undefined); // 报错:new value console.log(void 0); // 输出:undefined
上面的代码中,undefined被重新赋值后,全局的undefined就不再真正是undefined了。而void 0在任何情况下都保持是undefined,这样就避免了由于不小心改变了undefined而导致的错误。所以在某些代码中,为了写出更加安全的代码,会选择使用void 0来代替undefined。
现在的JavaScript版本已经不允许更改全局的undefined值,它现在是只读(read-only)的。对于现代版本的JavaScript(ECMAScript 5 及以后),试图给undefined赋值会被忽略(在严格模式下,试图给undefined赋值会抛出错误)。
在生成器函数或异步函数的上下文中,使用 void 0 作为参数传递给 next 方法,通常是因为你想要开始执行生成器,但不需要从 yield 表达式中接收任何值。这与传递 undefined 是等效的.
async/await 语法在 JavaScript 中提供了一种非常直观和简洁的方式来处理异步操作,它让异步代码看起来和同步代码非常相似,从而提高了代码的可读性和可维护性。然而,async/await 有一个缺点,即所谓的“异步传染性”。
async/await
这里的“异步传染性”指的是当你在函数内部使用 await 表达式时,你必须确保这个函数本身被声明为 async。这是因为 await 只能在 async 函数内部使用。这个要求确保了 await 表达式暂停执行的能力被正确地管理,并且任何在 await 表达式中抛出的错误都能被正确地捕获和处理。
await
async
想要解决“异步传染性”问题可以参考 这篇文章: js三座大山之异步四-Promise的同步调用消除异步的传染性
The text was updated successfully, but these errors were encountered:
No branches or pull requests
先有问题再有答案
如何理解async/await?
async/await有哪些使用场景?
对于不支持async/await的低版本babel如何实现的?
async/await与Generator是什么关系?
async/await与promise是什么关系
async/await与Iterator是什么关系
async/await的本质是什么?
前置文章
设计目的
async/await 的设计主要是为了简化JavaScript的异步编程和提高代码的可读性和可维护性。
在JavaScript中处理异步操作,早期主要依赖于回调函数(callbacks)和事件监听,但这导致代码很容易陷入“回调地狱”,即回调函数套回调函数,使得代码难以阅读和理解。
后来Promise应运而生,虽然它改善了异步操作的书写方式,避免了多层的回调嵌套,但在某些复杂的异步逻辑处理中依然显得累赘。
而async/await是基于Promise实现的,它通过语言层面提供了更为优雅的异步方案。在语法上,async/await使得异步代码的书写和普通的同步代码几乎没有差别,大大提高了代码可读性,也便于开发者理解代码的执行逻辑。
同时,使用async/await也使得对异步操作的错误处理变得更为方便,可以直接使用try/catch结构进行异常处理,这与同步代码中的错误处理方式一致,从而提高代码的一致性和可维护性。
关于异步方案的发展背景可以参考这篇文章 js三座大山之异步二异步方案
babel是如何编译async/await
兼容性
编译后代码
这里需要先掌握好前置知识再来阅读编译后的代码
将babel目标设置为Safari 10 代码如下:
这里对参数名做了些改动便于理解。
分析:
调试
asyncGeneratorStep 函数的作用:
asyncGeneratorStep
是一个辅助函数,用于处理生成器函数的每一步执行。它接收以下参数:iterator
: 生成器函数的迭代器。resolve
: 当生成器完成时,用于解决 Promise 的回调函数。reject
: 当生成器抛出错误时,用于拒绝 Promise 的回调函数。_next
和_throw
: 分别是生成器的next
和throw
方法的包装函数。next
: 表示当前操作是next
。v
: 传递给生成器next
方法的值, 也是yield的返回结果。当生成器函数执行到
yield
表达式时,它会暂停执行并返回一个对象,该对象包含value
属性(yield
表达式的结果)和done
属性(表示生成器是否完成)。asyncGeneratorStep
会根据done
属性的值来决定是继续执行生成器的下一条yield
表达式,还是解决 Promise。_asyncToGenerator 函数的作用:
_asyncToGenerator
是一个高阶函数,它接收一个生成器函数n
并返回一个新的函数。这个新函数在内部使用asyncGeneratorStep
来处理生成器的每一步。当调用这个新函数时,它会创建一个 Promise,并在生成器函数中使用_next
函数来启动生成器。每当生成器函数遇到yield
表达式时,_next
函数会被调用,并将yield
的结果作为参数传递给asyncGeneratorStep
。如果生成器函数抛出错误,_throw
函数会被调用来处理错误。_async 函数的作用:
_async
函数是一个使用_asyncToGenerator
转换的生成器函数。它定义了一个生成器,该生成器在每个yield
表达式处暂停,并接受一个值,这些值最终被加和并返回。这个函数通过_asyncToGenerator
转换后,返回了一个promise。asyncBabelPolyfill: 编译后的入口函数。
总结
babel将async/await转换为Generator函数,并且会自动遍历返回的迭代器,从
yield
表达式读取输入 并将结果通过next返回给调用方,然后封装为一个promise对象,当迭代器执行完毕,resolve这个promise对象,如果迭代过程中报错则reject这个promise。所以async/await的本质我们可以理解为:async/await = Generator + 自动遍历Iterator + Promise
关于 void 0
使用
void 0
时,实际上是在调用void
运算符并传递0
作为参数。由于void
运算符总是返回undefined
,所以void 0
的结果也是undefined
。使用
void 0
而不是直接写undefined
的一个原因是,undefined可以被重新赋值为其它值,而void 0一直保持是undefined。这样可以防止在那些可以改变undefined值的环境中出错。上面的代码中,undefined被重新赋值后,全局的undefined就不再真正是undefined了。而void 0在任何情况下都保持是undefined,这样就避免了由于不小心改变了undefined而导致的错误。所以在某些代码中,为了写出更加安全的代码,会选择使用void 0来代替undefined。
现在的JavaScript版本已经不允许更改全局的undefined值,它现在是只读(read-only)的。对于现代版本的JavaScript(ECMAScript 5 及以后),试图给undefined赋值会被忽略(在严格模式下,试图给undefined赋值会抛出错误)。
在生成器函数或异步函数的上下文中,使用
void 0
作为参数传递给next
方法,通常是因为你想要开始执行生成器,但不需要从yield
表达式中接收任何值。这与传递undefined
是等效的.async/await的缺陷
async/await
语法在 JavaScript 中提供了一种非常直观和简洁的方式来处理异步操作,它让异步代码看起来和同步代码非常相似,从而提高了代码的可读性和可维护性。然而,async/await
有一个缺点,即所谓的“异步传染性”。这里的“异步传染性”指的是当你在函数内部使用
await
表达式时,你必须确保这个函数本身被声明为async
。这是因为await
只能在async
函数内部使用。这个要求确保了await
表达式暂停执行的能力被正确地管理,并且任何在await
表达式中抛出的错误都能被正确地捕获和处理。想要解决“异步传染性”问题可以参考 这篇文章: js三座大山之异步四-Promise的同步调用消除异步的传染性
参考
The text was updated successfully, but these errors were encountered: