Có hai câu hỏi khá-biệt ở đây:
- Liệu hoặc không Node.js làm TCO?
- Điều năng suất huyền diệu này hoạt động như thế nào trong Node.js?
Có hay không Node.js làm TCO?
TL; DR: Không nữa, tính đến Node 8.x. Nó đã làm trong một thời gian, sau một lá cờ hay cách khác, nhưng khi viết bài này (tháng 11 năm 2017) nó không còn nữa bởi vì động cơ V8 JavaScript cơ bản mà nó sử dụng không hỗ trợ TCO nữa. Xem this answer để biết thêm về điều đó.
chi tiết:
Tail-gọi tối ưu hóa (TCO) là một đòi hỏi part of the ES2015 ("ES6") specification. Vì vậy, hỗ trợ nó không phải là, trực tiếp, một điều NodeJS, đó là một cái gì đó động cơ V8 V8 mà NodeJS sử dụng cần phải hỗ trợ.
Kể từ nút 8.x, V8 không hỗ trợ TCO, thậm chí không phải sau cờ. Nó có thể làm (một lần nữa) tại một số điểm trong tương lai; xem this answer để biết thêm về điều đó.
Nút 7.10 xuống còn 6.5.0 ít nhất (ghi chú của tôi nói 6.2, nhưng node.green không đồng ý) đã hỗ trợ TCO phía sau cờ (--harmony
trong 6.6.0 trở lên, --harmony_tailcalls
trước đó) ở chế độ nghiêm ngặt.
Nếu bạn muốn kiểm tra cài đặt của bạn, sau đây là các bài kiểm tra node.green sử dụng (hãy chắc chắn để sử dụng cờ nếu bạn đang sử dụng một phiên bản có liên quan):
function direct() {
"use strict";
return (function f(n){
if (n <= 0) {
return "foo";
}
return f(n - 1);
}(1e6)) === "foo";
}
function mutual() {
"use strict";
function f(n){
if (n <= 0) {
return "foo";
}
return g(n - 1);
}
function g(n){
if (n <= 0) {
return "bar";
}
return f(n - 1);
}
return f(1e6) === "foo" && f(1e6+1) === "bar";
}
console.log(direct());
console.log(mutual());
$ # Only certain versions of Node, notably not 8.x or (currently) 9.x; see above
$ node --harmony tco.js
true
true
như thế nào Điều này làm việc yield
ma thuật trong Node.js?
Đây là một điều ES2015 khác ("chức năng máy phát"), vì vậy một lần nữa đó là thứ mà V8 phải triển khai. Nó hoàn toàn được thực hiện trong phiên bản V8 trong Node 6.6.0 (và đã được cho một số phiên bản) và không phải là đằng sau bất kỳ cờ.
Chức năng của máy phát điện (được viết bằng function*
và sử dụng yield
) hoạt động bằng cách có thể dừng và trả lại một trình lặp ghi lại trạng thái của chúng và có thể được sử dụng để tiếp tục trạng thái của chúng trong một dịp tiếp theo. Alex Rauschmeyer có một bài viết chuyên sâu về chúng here.
Dưới đây là một ví dụ của việc sử dụng các iterator được trả về bởi các chức năng máy phát điện một cách rõ ràng, nhưng bạn thường sẽ không làm điều đó và chúng ta sẽ thấy lý do tại sao trong một thời điểm:
"use strict";
function* counter(from, to) {
let n = from;
do {
yield n;
}
while (++n < to);
}
let it = counter(0, 5);
for (let state = it.next(); !state.done; state = it.next()) {
console.log(state.value);
}
Đó có sản lượng này:
0
1
2
3
4
Sau đây là cách hoạt động:
- Khi chúng ta gọi là
counter
(let it = counter(0, 5);
), trạng thái nội bộ ban đầu của lệnh gọi tới counter
được khởi tạo và chúng tôi ngay lập tức lấy lại trình lặp; không có mã thực tế nào trong số counter
chạy (chưa).
- Gọi
it.next()
chạy mã trong counter
thông qua câu lệnh yield
đầu tiên. Tại thời điểm đó, counter
tạm dừng và lưu trữ trạng thái nội bộ của nó. it.next()
trả về đối tượng trạng thái có cờ done
và value
. Nếu cờ done
là false
, thì value
là giá trị được tạo bởi tuyên bố yield
.
- Mỗi cuộc gọi đến
it.next()
tiến hành trạng thái bên trong counter
sang yield
tiếp theo.
- Khi có cuộc gọi đến
it.next()
làm counter
kết thúc và trở lại, đối tượng trạng thái chúng ta nhận lại có done
thiết lập để true
và value
thiết lập để giá trị trả về của counter
.
Có biến cho iterator và đối tượng trạng thái và thực hiện cuộc gọi đến it.next()
và truy cập vào done
và value
tính là tất cả soạn sẵn đó (thường) được theo cách của những gì chúng ta đang cố gắng làm, vì vậy ES2015 cung cấp tuyên bố mới for-of
cho phép tất cả chúng tôi mang lại cho chúng tôi và chỉ cung cấp cho chúng tôi từng giá trị. Dưới đây là mã cùng viết ở trên với for-of
:
"use strict";
function* counter(from, to) {
let n = from;
do {
yield n;
}
while (++n < to);
}
for (let v of counter(0, 5)) {
console.log(v);
}
v
tương ứng với state.value
trong ví dụ trước đó của chúng tôi, với for-of
làm tất cả các cuộc gọi và it.next()
done
kiểm tra đối với chúng tôi.
Chạy nút có cờ '--harmony' để xem phiên bản thứ hai của bạn hoạt động như thế nào. ví dụ. 'node --harmony mytest.js'. Nhưng trước tiên hãy xem lại ví dụ bạn trích dẫn, bạn chỉ thích nghi một phần của nó với trường hợp của bạn. Về TCO câu hỏi thực sự là liệu V8 đã thực hiện nó - và không có đề cập đến rằng đang được thực hiện được nêu ra trong [v8 changelog] (https://code.google.com/p/v8/source/browse/trunk/ChangeLog) mà tôi có thể thấy. –
@ barry-johnson: Tôi đã thử sao chép các hàm mẫu bằng cách sử dụng '' yield'' trong liên kết thứ hai và Node.js có ngoại lệ đối với '' function * ''. Đây là một trong những lý do khiến tôi bối rối. –
Đó là lý do tại sao tôi nói bạn cần phải chạy nút với tùy chọn --harmony. Máy phát điện là một phần của ES6/Harmony, không phải là nút mặc định. –