Tôi đã nghe nói về một từ khóa "suất" trong JavaScript, nhưng tôi thấy tài liệu rất kém về nó. Ai đó có thể giải thích cho tôi (hoặc giới thiệu một trang web giải thích) việc sử dụng nó và nó được sử dụng để làm gì không?
Tôi đã nghe nói về một từ khóa "suất" trong JavaScript, nhưng tôi thấy tài liệu rất kém về nó. Ai đó có thể giải thích cho tôi (hoặc giới thiệu một trang web giải thích) việc sử dụng nó và nó được sử dụng để làm gì không?
Câu trả lời:
Các tài liệu MDN là khá tốt, IMO.
Hàm chứa từ khóa suất là một trình tạo. Khi bạn gọi nó, các tham số chính thức của nó bị ràng buộc với các đối số thực tế, nhưng cơ thể của nó không thực sự được đánh giá. Thay vào đó, một trình tạo vòng lặp được trả về. Mỗi lệnh gọi phương thức next () của trình tạo-iterator thực hiện một lần chuyển khác thông qua thuật toán lặp. Giá trị của mỗi bước là giá trị được chỉ định bởi từ khóa suất. Hãy nghĩ về năng suất như là phiên bản trả về của trình tạo-trình lặp, chỉ ra ranh giới giữa mỗi lần lặp của thuật toán. Mỗi lần bạn gọi next (), mã trình tạo lại tiếp tục từ câu lệnh theo sau sản lượng.
Trả lời muộn, có lẽ mọi người đều biết về yield
bây giờ, nhưng một số tài liệu tốt hơn đã xuất hiện.
Điều chỉnh một ví dụ từ "Tương lai của Javascript: Trình tạo" của James Long cho tiêu chuẩn Harmony chính thức:
function * foo(x) {
while (true) {
x = x * 2;
yield x;
}
}
"Khi bạn gọi foo, bạn nhận lại một đối tượng Trình tạo có phương thức tiếp theo."
var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16
Vì vậy, yield
là loại giống như return
: bạn sẽ có được một cái gì đó trở lại. return x
trả về giá trị của x
, nhưng yield x
trả về một hàm, cung cấp cho bạn một phương thức để lặp lại giá trị tiếp theo. Hữu ích nếu bạn có một thủ tục cần nhiều bộ nhớ mà bạn có thể muốn làm gián đoạn trong quá trình lặp.
function* foo(x){
ở đó
*
. Bạn có cần hay không tùy thuộc vào loại tương lai bạn sẽ trở lại. Chi tiết rất dài: GvR giải thích nó cho việc triển khai Python , theo đó việc triển khai Javascript được mô hình hóa. Sử dụng function *
sẽ luôn luôn đúng, mặc dù trong một số trường hợp hơi quá phí so function
với yield
.
function *
và yield
và thêm lỗi được trích dẫn ("Lỗi sớm được đưa ra nếu biểu thức sản lượng hoặc sản lượng * xảy ra trong hàm không tạo"). Nhưng, việc triển khai Javascript 1.7 ban đầu trong Firefox không yêu cầu*
. Cập nhật câu trả lời phù hợp. Cảm ơn!
Nó thực sự đơn giản, đây là cách nó hoạt động
yield
từ khóa chỉ đơn giản là giúp tạm dừng và tiếp tục một chức năng bất cứ lúc nào không đồng bộ .Thực hiện chức năng tạo đơn giản này :
function* process() {
console.log('Start process 1');
console.log('Pause process2 until call next()');
yield;
console.log('Resumed process2');
console.log('Pause process3 until call next()');
let parms = yield {age: 12};
console.log("Passed by final process next(90): " + parms);
console.log('Resumed process3');
console.log('End of the process function');
}
let _ process = process ();
Cho đến khi bạn gọi _ process.next () nó sẽ không thực thi 2 dòng mã đầu tiên , thì sản lượng đầu tiên sẽ tạm dừng chức năng. Để tiếp tục chức năng cho đến điểm tạm dừng tiếp theo ( từ khóa suất ), bạn cần gọi _ process.next () .
Bạn có thể nghĩ nhiều sản lượng là điểm dừng trong trình gỡ lỗi javascript trong một chức năng. Cho đến khi bạn nói để điều hướng điểm dừng tiếp theo, nó sẽ không thực thi khối mã. ( Lưu ý : không chặn toàn bộ ứng dụng)
Nhưng trong khi năng suất Thực hiện tạm dừng này và tiếp tục hành vi đó có thể trở lại một số kết quả cũng {value: any, done: boolean}
theo chức năng trước đây, chúng tôi đã không phát ra bất kỳ giá trị. Nếu chúng ta khám phá đầu ra trước đó, nó sẽ hiển thị tương tự { value: undefined, done: false }
với giá trị không xác định .
Hãy đào sâu vào từ khóa năng suất. Tùy chọn bạn có thể thêm biểu thức và đặt gán một giá trị tùy chọn mặc định . (Cú pháp tài liệu chính thức)
[rv] = yield [expression];
biểu thức : Giá trị trả về từ hàm tạo
yield any;
yield {age: 12};
rv : Trả về giá trị tùy chọn được truyền cho phương thức next () của trình tạo
Đơn giản là bạn có thể truyền tham số cho hàm process () với cơ chế này, để thực thi các phần năng suất khác nhau.
let val = yield 99;
_process.next(10);
now the val will be 10
Tập quán
Người giới thiệu:
Đơn giản hóa / xây dựng dựa trên câu trả lời của Nick Sotiros (mà tôi nghĩ là tuyệt vời), tôi nghĩ tốt nhất là mô tả cách người ta sẽ bắt đầu viết mã yield
.
Theo tôi, ưu điểm lớn nhất của việc sử dụng yield
là nó sẽ loại bỏ tất cả các vấn đề gọi lại lồng nhau mà chúng ta thấy trong mã. Thật khó để thấy làm thế nào lúc đầu, đó là lý do tại sao tôi quyết định viết câu trả lời này (cho bản thân tôi và hy vọng những người khác!)
Cách thức thực hiện là bằng cách đưa ra ý tưởng về đồng quy, đây là chức năng có thể tự nguyện dừng / tạm dừng cho đến khi nhận được những gì nó cần. Trong javascript, điều này được ký hiệu là function*
. Chỉ các function*
chức năng có thể sử dụng yield
.
Đây là một số javascript điển hình:
loadFromDB('query', function (err, result) {
// Do something with the result or handle the error
})
Điều này thật rắc rối bởi vì bây giờ tất cả mã của bạn (rõ ràng cần phải chờ loadFromDB
cuộc gọi này ) cần phải nằm trong cuộc gọi lại trông xấu xí này. Điều này là xấu vì một vài lý do ...
})
mà bạn cần theo dõi ở khắp mọi nơifunction (err, result)
biệt ngữ thêm nàyresult
Mặt khác, với yield
, tất cả những điều này có thể được thực hiện trong một dòng với sự trợ giúp của khung công tác hợp tác tốt đẹp.
function* main() {
var result = yield loadFromDB('query')
}
Và vì vậy, bây giờ chức năng chính của bạn sẽ mang lại khi cần thiết khi nó cần chờ các biến và những thứ được tải. Nhưng bây giờ, để chạy cái này, bạn cần gọi một hàm bình thường (hàm không coroutine). Một khung công tác đồng thường đơn giản có thể khắc phục vấn đề này để tất cả những gì bạn phải làm là chạy nó:
start(main())
Và bắt đầu được xác định (từ câu trả lời của Nick Sotiro)
function start(routine, data) {
result = routine.next(data);
if(!result.done) {
result.value(function(err, data) {
if(err) routine.throw(err); // continue next iteration of routine with an exception
else start(routine, data); // continue next iteration of routine normally
});
}
}
Và bây giờ, bạn có thể có mã đẹp dễ đọc hơn nhiều, dễ xóa và không cần phải sử dụng thụt lề, hàm, v.v.
Một quan sát thú vị là trong ví dụ này, yield
thực sự chỉ là một từ khóa bạn có thể đặt trước một hàm với một cuộc gọi lại.
function* main() {
console.log(yield function(cb) { cb(null, "Hello World") })
}
Sẽ in "Xin chào thế giới". Vì vậy, bạn thực sự có thể biến bất kỳ hàm gọi lại nào thành sử dụng bằng yield
cách chỉ cần tạo cùng một chữ ký hàm (không có cb) và trả về function (cb) {}
, như vậy:
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
Hy vọng với kiến thức này bạn có thể viết mã sạch hơn, dễ đọc hơn và dễ xóa !
function*
chỉ là một chức năng thông thường mà không có năng suất?
function *
là một chức năng có chứa năng suất. Đó là một chức năng đặc biệt gọi là máy phát điện.
yield
ở mọi nơi, tôi chắc chắn rằng điều này có ý nghĩa hơn so với các cuộc gọi lại, nhưng tôi không thấy cách này dễ đọc hơn cuộc gọi lại.
Để đưa ra một câu trả lời hoàn chỉnh: yield
đang hoạt động tương tự return
, nhưng trong một máy phát điện.
Đối với ví dụ thường được đưa ra, điều này hoạt động như sau:
function *squareGen(x) {
var i;
for (i = 0; i < x; i++) {
yield i*i;
}
}
var gen = squareGen(3);
console.log(gen.next().value); // prints 0
console.log(gen.next().value); // prints 1
console.log(gen.next().value); // prints 4
Nhưng cũng có một mục đích thứ hai của từ khóa năng suất. Nó có thể được sử dụng để gửi các giá trị đến trình tạo.
Để làm rõ, một ví dụ nhỏ:
function *sendStuff() {
y = yield (0);
yield y*y;
}
var gen = sendStuff();
console.log(gen.next().value); // prints 0
console.log(gen.next(2).value); // prints 4
Điều này hoạt động, như giá trị 2
được gán cho y
, bằng cách gửi nó đến trình tạo, sau khi nó dừng ở mức sản lượng đầu tiên (trả về 0
).
Điều này cho phép chúng tôi để một số công cụ thực sự thú vị. (tra cứu coroutine)
Nó được sử dụng cho các trình tạo vòng lặp. Về cơ bản, nó cho phép bạn tạo một chuỗi (có khả năng vô hạn) bằng cách sử dụng mã thủ tục. Xem tài liệu của Mozilla .
yield
cũng có thể được sử dụng để loại bỏ địa ngục gọi lại, với khung coroutine.
function start(routine, data) {
result = routine.next(data);
if(!result.done) {
result.value(function(err, data) {
if(err) routine.throw(err); // continue next iteration of routine with an exception
else start(routine, data); // continue next iteration of routine normally
});
}
}
// with nodejs as 'node --harmony'
fs = require('fs');
function read(path) {
return function(callback) { fs.readFile(path, {encoding:'utf8'}, callback); };
}
function* routine() {
text = yield read('/path/to/some/file.txt');
console.log(text);
}
// with mdn javascript 1.7
http.get = function(url) {
return function(callback) {
// make xhr request object,
// use callback(null, resonseText) on status 200,
// or callback(responseText) on status 500
};
};
function* routine() {
text = yield http.get('/path/to/some/file.txt');
console.log(text);
}
// invoked as.., on both mdn and nodejs
start(routine());
Trình tạo chuỗi Fibonacci sử dụng từ khóa năng suất.
function* fibbonaci(){
var a = -1, b = 1, c;
while(1){
c = a + b;
a = b;
b = c;
yield c;
}
}
var fibonacciGenerator = fibbonaci();
fibonacciGenerator.next().value; // 0
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 2
Yeild
từ khóa trong hàm javaScript làm cho nó tạo ra,
trình tạo trong javaScript là gì?
Trình tạo là một hàm tạo ra một chuỗi kết quả thay vì một giá trị duy nhất, tức là bạn tạo ra một chuỗi các giá trị
Có nghĩa là các trình tạo giúp chúng ta làm việc không đồng bộ với các trình vòng lặp trợ giúp, Oh bây giờ các trình vòng lặp hack là gì? có thật không?
Lặp đi lặp lại có nghĩa là thông qua đó chúng ta có thể truy cập từng mục một
từ nơi iterator giúp chúng ta truy cập từng mục một? nó giúp chúng ta truy cập các mục thông qua các chức năng của trình tạo
các hàm tạo là những hàm mà chúng ta sử dụng yeild
từ khóa, từ khóa năng suất giúp chúng ta tạm dừng và tiếp tục thực thi hàm
đây là ví dụ nhanh
function *getMeDrink() {
let question1 = yield 'soda or beer' // execution will pause here because of yield
if (question1 == 'soda') {
return 'here you get your soda'
}
if (question1 == 'beer') {
let question2 = yield 'Whats your age' // execution will pause here because of yield
if (question2 > 18) {
return "ok you are eligible for it"
} else {
return 'Shhhh!!!!'
}
}
}
let _getMeDrink = getMeDrink() // initialize it
_getMeDrink.next().value // "soda or beer"
_getMeDrink.next('beer').value // "Whats your age"
_getMeDrink.next('20').value // "ok you are eligible for it"
_getMeDrink.next().value // undefined
hãy để tôi giải thích những gì đang xảy ra
bạn nhận thấy việc thực thi đang bị tạm dừng tại mỗi yeild
từ khóa và chúng tôi có thể truy cập trước yield
với sự trợ giúp của iterator.next()
điều này lặp lại cho tất cả các yield
từ khóa một lần và sau đó trả về không xác định khi không còn yield
từ khóa nào trong những từ đơn giản mà bạn có thể nóiyield
từ khóa là điểm dừng trong đó chức năng mỗi lần tạm dừng và chỉ tiếp tục khi gọi nó bằng cách sử dụng iterator
đối với trường hợp của chúng tôi: _getMeDrink.next()
đây là ví dụ về trình vòng lặp giúp chúng tôi truy cập từng điểm dừng trong chức năng
Ví dụ về Máy phát điện:
async/await
nếu bạn thấy việc thực hiện async/await
bạn sẽ thấy generator functions & promises
được sử dụng để thực hiện async/await
công việc
xin vui lòng chỉ ra bất kỳ đề nghị được hoan nghênh
Sự phụ thuộc giữa các cuộc gọi javascript async.
Một ví dụ khác về cách sản lượng có thể được sử dụng.
function request(url) {
axios.get(url).then((reponse) => {
it.next(response);
})
}
function* main() {
const result1 = yield request('http://some.api.com' );
const result2 = yield request('http://some.otherapi?id=' + result1.id );
console.log('Your response is: ' + result2.value);
}
var it = main();
it.next()
Trước khi bạn tìm hiểu về năng suất, bạn cần biết về máy phát điện. Máy phát điện được tạo bằng function*
cú pháp. Các hàm tạo không thực thi mã mà thay vào đó trả về một kiểu trình vòng lặp được gọi là trình tạo. Khi một giá trị được đưa ra bằng next
phương thức, hàm tạo sẽ tiếp tục thực thi cho đến khi gặp một từ khóa năng suất. Việc sử dụng yield
mang lại cho bạn một đối tượng chứa hai giá trị, một là giá trị và đối tượng kia được thực hiện (boolean). Giá trị có thể là một mảng, đối tượng, vv
Một ví dụ đơn giản:
const strArr = ["red", "green", "blue", "black"];
const strGen = function*() {
for(let str of strArr) {
yield str;
}
};
let gen = strGen();
for (let i = 0; i < 5; i++) {
console.log(gen.next())
}
//prints: {value: "red", done: false} -> 5 times with different colors, if you try it again as below:
console.log(gen.next());
//prints: {value: undefined, done: true}
Tôi cũng đang cố gắng để hiểu từ khóa năng suất. Dựa trên hiểu biết hiện tại của tôi, trong trình tạo, từ khóa năng suất hoạt động giống như bộ chuyển đổi ngữ cảnh CPU. Khi câu lệnh năng suất được chạy, tất cả các trạng thái (ví dụ: biến cục bộ) được lưu.
Bên cạnh đó, một đối tượng kết quả trực tiếp sẽ được trả về cho người gọi, như {value: 0, xong: false}. Người gọi có thể sử dụng đối tượng kết quả này để quyết định xem có nên 'đánh thức' lại trình tạo hay không bằng cách gọi next () (gọi next () là để lặp lại quá trình thực thi).
Một điều quan trọng khác là nó có thể đặt giá trị cho biến cục bộ. Giá trị này có thể được chuyển qua bởi người gọi 'next ()' khi 'đánh thức' máy phát. ví dụ: it.next ('valueToPass'), như thế này: "resultValue = ieldQueryQuery (1);" Giống như khi đánh thức một lần thực hiện tiếp theo, người gọi có thể đưa một số kết quả đang chạy vào thực thi (đưa nó vào biến cục bộ). Vì vậy, đối với việc thực hiện này, có hai loại trạng thái:
bối cảnh đã lưu trong lần thực hiện cuối cùng.
Các giá trị được chèn bởi trình kích hoạt này.
Vì vậy, với tính năng này, trình tạo có thể sắp xếp nhiều hoạt động không đồng bộ. Kết quả của truy vấn không đồng bộ đầu tiên sẽ được chuyển sang truy vấn thứ hai bằng cách đặt biến cục bộ (resultValue trong ví dụ trên). Truy vấn async thứ hai chỉ có thể được kích hoạt bởi phản hồi của truy vấn async đầu tiên. Sau đó, truy vấn async thứ hai có thể kiểm tra giá trị biến cục bộ để quyết định các bước tiếp theo vì biến cục bộ là giá trị được chèn từ phản hồi của truy vấn đầu tiên.
Những khó khăn của truy vấn không đồng bộ là:
gọi lại địa ngục
mất bối cảnh trừ khi chuyển chúng dưới dạng tham số trong cuộc gọi lại.
năng suất và máy phát điện có thể giúp cả hai.
Không có năng suất và trình tạo, để sắp xếp nhiều truy vấn không đồng bộ yêu cầu gọi lại lồng nhau với các tham số là bối cảnh không dễ đọc và duy trì.
Dưới đây là một ví dụ truy vấn async được xâu chuỗi đang chạy với nodejs:
const axios = require('axios');
function slowQuery(url) {
axios.get(url)
.then(function (response) {
it.next(1);
})
.catch(function (error) {
it.next(0);
})
}
function* myGen(i=0) {
let queryResult = 0;
console.log("query1", queryResult);
queryResult = yield slowQuery('https://google.com');
if(queryResult == 1) {
console.log("query2", queryResult);
//change it to the correct url and run again.
queryResult = yield slowQuery('https://1111111111google.com');
}
if(queryResult == 1) {
console.log("query3", queryResult);
queryResult = yield slowQuery('https://google.com');
} else {
console.log("query4", queryResult);
queryResult = yield slowQuery('https://google.com');
}
}
console.log("+++++++++++start+++++++++++");
let it = myGen();
let result = it.next();
console.log("+++++++++++end+++++++++++");
Dưới đây là kết quả chạy:
+++++++++++ bắt đầu +++++++++++
truy vấn1 0
+++++++++++ kết thúc +++++++++++
truy vấn2 1
truy vấn4 0
Dưới đây mẫu trạng thái có thể làm điều tương tự cho ví dụ trên:
const axios = require('axios');
function slowQuery(url) {
axios.get(url)
.then(function (response) {
sm.next(1);
})
.catch(function (error) {
sm.next(0);
})
}
class StateMachine {
constructor () {
this.handler = handlerA;
this.next = (result = 1) => this.handler(this, result);
}
}
const handlerA = (sm, result) => {
const queryResult = result; //similar with generator injection
console.log("query1", queryResult);
slowQuery('https://google.com');
sm.handler = handlerB; //similar with yield;
};
const handlerB = (sm, result) => {
const queryResult = result; //similar with generator injection
if(queryResult == 1) {
console.log("query2", queryResult);
slowQuery('https://1111111111google.com');
}
sm.handler = handlerC; //similar with yield;
};
const handlerC = (sm, result) => {
const queryResult = result; //similar with generator injection;
if (result == 1 ) {
console.log("query3", queryResult);
slowQuery('https://google.com');
} else {
console.log("query4", queryResult);
slowQuery('https://google.com');
}
sm.handler = handlerEnd; //similar with yield;
};
const handlerEnd = (sm, result) => {};
console.log("+++++++++++start+++++++++++");
const sm = new StateMachine();
sm.next();
console.log("+++++++++++end+++++++++++");
Sau đây là kết quả hoạt động:
+++++++++++ bắt đầu +++++++++++
truy vấn1 0
+++++++++++ kết thúc +++++++++++
truy vấn2 1
truy vấn4 0
đừng quên cú pháp 'x của trình tạo' rất hữu ích để lặp qua trình tạo. Không cần sử dụng hàm next ().
function* square(x){
for(i=0;i<100;i++){
x = x * 2;
yield x;
}
}
var gen = square(2);
for(x of gen){
console.log(x);
}