Last Commit: 2024-01-06 17:50:27
views:
Table of Content
Generator
Basic usage
Compared to normal Function, generator function must has a *
, and has its special property: next
.
Like in Python, yield
means to relinquish the control. It makes the function is possible to stop, and restart by the controller.
There is a simple example:
function* gen(arr) {
for (let i of arr){
yield i;
}
}
var it = gen([1,2,3]);
it.next(); //value: 1, done: false
it.next(); //value: 2, done: false
it.next(); //value: 3, done: false
it.next(); //value: undefined, done: true
In addition, in next
, it can put a new parameter like as below:
function* gen(arr) {
for (let i of arr){
var result = yield i;
console.log(result);
}
}
var it = gen([1,2,3]);
it.next(1); //
it.next(2); //2
it.next(3); //3
it.next(4); //4
As we can see, the first it.next(1)
did not log anything. The reason is that in generator, the next
will only execute to yield
, so in the first next
, the variable result
is not assigned. To prove it:
function* gen(arr) {
var container ={};
Object.defineProperty(container, 'result', {
set:function () {
console.log('result is defined');
}
});
for (let i of arr){
container.result = yield i;
console.log(container.result);
}
}
var it = gen([1,2,3]);
it.next(1); //
it.next(2); //result is defined
it.next(3); //result is defined
it.next(4); //result is defined
async and await
Firstly, let's see how typescript to transfer it, see in here which is based on ES5:
//origin
const run = async () => {
await 'start';
const a = await 'finished';
return a;
}
// transfer. remove some irrelevant codes
var __awaiter = function (generator) {
function adopt(value) {
return value instanceof Promise
? value
: new Promise(function (resolve) {
resolve(value);
});
}
return new Promise(function (resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done
? resolve(result.value)
: adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator()).next());
});
};
const run = () =>
__awaiter(function* () {
yield "start";
const a = yield "finished";
return a;
});
As we can see, babel use generator + Promise
to transfer aysnc
+ await
, as yield
can wait until the Promise
finished.
There is a more simple function, but the principle is the same:
function* gen(task, pages) {
for (let page of pages){
yield task(page);
}
}
var it = gen(task, [1,2]);
while (true){
let result = it.next();
result.value.then(data=>{
console.log(data);
});
if (result.done){
break;
}
}
In Addition, if we want to transfer async/await to compatible with ES3, the compiler will add a generator
help function.
See a demo:
async function test() {
return new Promise((r) => {
console.log(0);
r();
});
}
async function run() {
await test();
console.log(1);
}
run();
new Promise((r) => {
r();
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(4);
})
// 0 2 3 1 4
There will be two promises when await test()
!.