异步/等待和 ES6 与生成器的产量之间的区别
Difference between async/await and ES6 yield with generators
我刚刚读了这篇精彩的文章"生成器",它清楚地突出了这个函数,这是一个用于处理生成器函数的辅助函数:
function async(makeGenerator){
return function () {
var generator = makeGenerator.apply(this, arguments);
function handle(result){
// result => { done: [Boolean], value: [Object] }
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value).then(function (res){
return handle(generator.next(res));
}, function (err){
return handle(generator.throw(err));
});
}
try {
return handle(generator.next());
} catch (ex) {
return Promise.reject(ex);
}
}
}
我假设这或多或少是使用 async
/await
实现 async
关键字的方式。所以问题是,如果是这样的话,那么await
关键字和yield
关键字之间的区别到底是什么?await
总是把某事变成承诺,而yield
却没有这样的保证吗?这是我最好的猜测!
您还可以在本文中看到 async
/await
与生成器yield
相似之处,他描述了"生成"函数 ES7 异步函数。
好吧,事实证明,async
/await
和生成器之间存在非常密切的关系。我相信async
/await
将永远建立在发电机上。如果你看看 Babel 的转译方式 async
/await
:
巴别塔是这样说的:
this.it('is a test', async function () {
const foo = await 3;
const bar = await new Promise(resolve => resolve('7'));
const baz = bar * foo;
console.log(baz);
});
并把它变成这个
function _asyncToGenerator(fn) {
return function () {
var gen = fn.apply(this, arguments);
return new Promise(function (resolve, reject) {
function step(key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
return Promise.resolve(value).then(function (value) {
return step("next", value);
}, function (err) {
return step("throw", err);
});
}
}
return step("next");
});
};
}
this.it('is a test', _asyncToGenerator(function* () { // << now it's a generator
const foo = yield 3; // <<< now it's yield, not await
const bar = yield new Promise(resolve => resolve(7));
const baz = bar * foo;
console.log(baz);
}));
你算一算。
这使得看起来async
关键字只是那个包装器函数,但如果是这样的话,那么await
就会变成yield
,以后当它们成为本机时,图片可能会更多。
您可以在此处查看更多解释:https://www.promisejs.org/generators/
yield
可以被认为是await
的构建块。 yield
获取给定的值并将其传递给调用方。然后,调用方可以对该值 (1( 执行任何它想要的操作。稍后,调用方可能会将一个值返回给生成器(通过 generator.next()
(,该值将成为yield
表达式 (2( 的结果,或者看起来像是由yield
表达式 (3( 抛出的错误。
async
- await
可以考虑使用yield
。在 (1( 处,调用者(即 async
- await
驱动程序 - 类似于您发布的函数(将使用与 new Promise(r => r(value)
类似的算法将值包装在 promise 中(注意,不是 Promise.resolve
,但这没什么大不了的(。然后,它等待承诺解决。如果它满足,它将在 (2( 处传递回已履行的值。如果它拒绝,它会在 (3( 处将拒绝原因作为错误抛出。
因此,async
- await
的效用是这种机器,它使用yield
将生成的值作为承诺解包,并将其解析的值传回,重复直到函数返回其最终值。
await
关键字和yield
关键字之间到底有什么区别?
await
关键字只能在 async function
s 中使用,而 yield
关键字只能在生成器 function*
s 中使用。这些显然也不同 - 一个返回承诺,另一个返回生成器。
await
总是把某事变成承诺,而yield
却没有这样的保证吗?
是的,await
会根据等待的值调用Promise.resolve
。
yield
只是在生成器之外产生值。
tl;dr
使用async
/await
99%的时间超过发电机。为什么?
-
async
/await
直接取代了承诺链最常见的工作流程,允许代码像同步一样声明,大大简化了它。 -
生成器抽象了一个用例,在该用例中,您将调用一系列相互依赖的异步操作,最终将处于"完成"状态。最简单的示例是分页浏览最终返回最后一个集合的结果,但您只会根据需要调用页面,而不是立即连续调用。
-
async
/await
实际上是一个建立在生成器之上的抽象,使使用承诺更容易。
查看非常深入的异步/等待与生成器的解释
试试这个测试程序,我曾经理解过await
/async
的承诺。
程序#1:没有承诺,它不会按顺序运行
function functionA() {
console.log('functionA called');
setTimeout(function() {
console.log('functionA timeout called');
return 10;
}, 15000);
}
function functionB(valueA) {
console.log('functionB called');
setTimeout(function() {
console.log('functionB timeout called = ' + valueA);
return 20 + valueA;
}, 10000);
}
function functionC(valueA, valueB) {
console.log('functionC called');
setTimeout(function() {
console.log('functionC timeout called = ' + valueA);
return valueA + valueB;
}, 10000);
}
async function executeAsyncTask() {
const valueA = await functionA();
const valueB = await functionB(valueA);
return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
console.log('response called = ' + response);
});
console.log('program ended');
计划#2:承诺
function functionA() {
return new Promise((resolve, reject) => {
console.log('functionA called');
setTimeout(function() {
console.log('functionA timeout called');
// return 10;
return resolve(10);
}, 15000);
});
}
function functionB(valueA) {
return new Promise((resolve, reject) => {
console.log('functionB called');
setTimeout(function() {
console.log('functionB timeout called = ' + valueA);
return resolve(20 + valueA);
}, 10000);
});
}
function functionC(valueA, valueB) {
return new Promise((resolve, reject) => {
console.log('functionC called');
setTimeout(function() {
console.log('functionC timeout called = ' + valueA);
return resolve(valueA + valueB);
}, 10000);
});
}
async function executeAsyncTask() {
const valueA = await functionA();
const valueB = await functionB(valueA);
return functionC(valueA, valueB);
}
console.log('program started');
executeAsyncTask().then(function(response) {
console.log('response called = ' + response);
});
console.log('program ended');
在许多方面,生成器是 async/await 的超集。现在 async/await 具有比 co 更干净的堆栈跟踪,co是最受欢迎的基于 async/await 的生成器库。您可以使用生成器实现自己的 async/await 风格并添加新功能,例如内置支持对非承诺的yield
或在 RxJS 可观察量上构建它。
因此,简而言之,生成器为您提供了更大的灵活性,而基于生成器的库通常具有更多功能。但是 async/await 是该语言的核心部分,它是标准化的,不会在你的领导下改变,你不需要库来使用它。我有一篇博客文章,详细介绍了异步/等待和生成器之间的区别。
yield
+ gen.next()
-as-a-language-功能可用于描述(或实现(await-async
抽象出来的底层控制流。
正如其他答案所暗示的那样,await
即语言功能是(或可以认为(基于yield
的实现。
以下是对此更直观的理解:
假设我们在异步函数中有 42 个awaits
,await A -> await B -> ...
在内心深处,它相当于拥有yield A -> tries resolve this as a Promise
[1]
-> if resolvable, we yield B, and repeat [1] for B
-> if not resolveable, we throw
因此,我们最终在发电机中获得了42 yields
。在我们的控制器中,我们只是继续执行gen.next()
,直到它完成或被拒绝。(即这与在包含 42 个await
的异步函数上使用 await
相同。
这就是为什么像 redux-saga 这样的 lib 利用 generator 将 promise 传送到saga 中间件,以便在一个地方解决;从而将 Promise 结构与其评估分离,从而与 Free Monad 非常相似。
这个想法是递归地链接then()
调用以复制await
的行为,从而允许人们以同步方式调用async
例程。生成器函数用于将控制权(和每个值(从被调用方返回给调用方,这恰好是_asyncToGenerator()
包装函数。
如上所述,这是 Babel 用来创建 polyfill 的技巧。我稍微编辑了代码以使其更具可读性并添加了注释。
(async function () {
const foo = await 3;
const bar = await new Promise((resolve) => resolve(7));
const baz = bar * foo;
console.log(baz);
})();
function _asyncToGenerator(fn) {
return function () {
let gen = fn(); // Start the execution of the generator function and store the generator object.
return new Promise(function (resolve, reject) {
function step(func, arg) {
try {
let item = gen[func](arg); // Retrieve the function object from the property name and invoke it. Similar to eval(`gen.${func}(arg)`) but safer. If the next() method is called on the generator object, the item value by the generator function is saved and the generator resumes execution. The value passed as an argument is assigned as a result of a yield expression.
if (item.done) {
resolve(item.value);
return; // The executor return value is ignored, but we need to stop the recursion here.
}
// The trick is that Promise.resolve() returns a promise object that is resolved with the value given as an argument. If that value is a promise object itself, then it's simply returned as is.
return Promise.resolve(item.value).then(
(v) => step("next", v),
(e) => step("throw", e)
);
} catch (e) {
reject(e);
return;
}
}
return step("next");
});
};
}
_asyncToGenerator(function* () { // <<< Now it's a generator function.
const foo = yield 3; // <<< Now it's yield, not await.
const bar = yield new Promise((resolve, reject) => resolve(7)); // <<< Each item is converted to a thenable object and recursively enclosed into chained then() calls.
const baz = bar * foo;
console.log(baz);
})();
- 全局变量和全局对象的属性之间有什么区别吗
- JavaScript中的函数和对象之间没有区别吗?
- 在下划线中使用_(obj).map(callback)和_.map(obj,callback)之间的区别
- javascript函数的:和=之间的区别
- 什么's本地node.js服务器和python简单http服务器之间的区别
- 函数中this和var之间的区别
- “util.inherits”和在NodeJS中扩展原型之间的区别
- Math.min()和Math.max()之间有什么区别?在Javascript中
- webpack开发模式和生产构建模式之间有什么区别
- servlet和代理servlet之间的区别
- Javascript 类型未定义和 void 之间的区别
- Javascript-defineProperty和直接在对象上定义函数之间的区别
- 关于承诺/A+规范,术语“当时可”和“承诺”之间有什么区别
- 布局引擎和javascript引擎之间的区别
- 什么's extjs中的mon()和on()之间的区别
- type=text/javascript和language=javascript之间的区别
- 函数()和新函数()之间的区别
- JavaScript中let和var之间的区别
- 蓝鸟的done()和spread()之间的区别
- Node.js HTTP/NET——连接和请求之间的区别