实现Async/Await
要挑战的任务是使用JavaScript的generator生成器来实现Async/Await。
问题描述
下面是一个Async/Await函数的示例。
async function doSomething(value) {
const result1 = await fetchFromNetwork(value + '-1');
const result2 = await fetchFromNetwork(value + '-2');
try {
const result3 = await failedFetchFromNetwork();
} catch (error) {
console.error('Error fetching from network');
}
return result1 + ' ' + result2;
}
doSomething('http://google.com')
.then(r => console.log(`Got result: ${
r}`))
.catch(console.error)
我们需要使用generator生成器和一个特别的封装函数“asynk”来实现同样功能。等效的示例为:
const doSomething = asynk(function* (value) {
const result1 = yield fetchFromNetwork(value + '-1');
const result2 = yield fetchFromNetwork(value + '-2');
try {
const result3 = yield failedFetchFromNetwork();
} catch (error) {
console.error('Error fetching from network');
}
return result1 + ' ' + result2;
});
doSomething('http://google.com')
.then(r => console.log(`Got result: ${
r}`))
.catch(console.error)
关于“asynk“的注意事项:
- 它接收一个generator生成器函数并返回一个新函数;
- 当返回的函数被调用时,它应该返回一个Promise期约。Promise期约应当对generator生成器函数的返回值有所处理;
- 返回函数的类型特征应该和传入generator生成器函数的类型特征匹配。唯一的例外是,如果generator生成器函数返回一个非Promise期约的类型,返回函数应该返回一个与那个类型相对应的Promise期约。
待处理事项
- 如果愿意的话,你可以先实现无类型的方案。有些人觉得使用类型有所帮助,另外一些人则觉得后续添加类型更容易;
- 先关注控制流,然后才是参数值返回值可能有所帮助。
规则
- 你不能使用原生的Async/Await;
- 请不要直接查阅与”如何使用generator生成器实现Async/Await“相关的网上资料。
参考文献
下面是一些你会觉得有用的链接。请悉听尊便访问这些链接,但务必仅限于此。
-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator
-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next
下面的类型定义或许有所裨益:
interface Generator<T = unknown, TReturn = any, TNext = unknown> extends Iterator<T, TReturn, TNext> {
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return(value: TReturn): IteratorResult<T, TReturn>;
throw(e: any): IteratorResult<T, TReturn>;
[Symbol.iterator](): Generator<T, TReturn, TNext>;
}
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
TypeScript代码模板:
// See README.md for instructions.
// TODO: add type annotations.
function asynk(fn: any) {
// YOUR CODE HERE
}
// Playgroud for testing the code.
console.clear();
function* countUp() {
for (let i = 0; i < 10; i++) {
yield i;
}
}
const g = countUp();
console.log(g.next());
console.log(g.next());
// const playground = asynk(function* () {
// const result = yield Promise.resolve('hello');
// });
// playground().catch(console.error)
最终实现:
无类型的JavaScript版本:
function asynk(fn) {
// YOUR CODE HERE
return (...args) =>
new Promise((resolve, reject) => {
// Initialize the generator function, which might have signatures,
// Extract the next() queue fisrt, then iterate another initialized generator
const runner = fn(...args);
try {
let promiseCallbackQueue = [
(res, slaveRunner) => slaveRunner.next(res).value,
];
let result = runner.next();
// First round, collect promiseCallback
while (!result.done && String(result.value) === '[object Promise]') {
promiseCallbackQueue.push(