Cập nhật (2017)
Ở đây vào năm 2017, Promises được tích hợp vào JavaScript, chúng được thêm vào bởi thông số kỹ thuật ES2015 (polyfills có sẵn cho các môi trường lỗi thời như IE8-IE11). Cú pháp mà chúng đi cùng sử dụng một lệnh gọi lại mà bạn truyền vào hàm Promise
tạo (trình Promise
thực thi ), hàm này nhận các hàm để giải quyết / từ chối lời hứa dưới dạng đối số.
Đầu tiên, vì async
bây giờ có một ý nghĩa trong JavaScript (mặc dù nó chỉ là một từ khóa trong một số ngữ cảnh nhất định), tôi sẽ sử dụng later
làm tên của hàm để tránh nhầm lẫn.
Độ trễ cơ bản
Sử dụng các hứa hẹn gốc (hoặc một polyfill trung thực), nó sẽ giống như sau:
function later(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
Lưu ý rằng, cho rằng một phiên bản của setTimeout
đó là phù hợp với định nghĩa cho các trình duyệt mà setTimeout
không vượt qua bất kỳ đối số để gọi lại trừ khi bạn cung cấp cho họ sau khi khoảng thời gian (điều này có thể không đúng trong môi trường phi trình duyệt, và không sử dụng được đúng trên Firefox, nhưng bây giờ là đúng; nó đúng trên Chrome và thậm chí là trên IE8).
Độ trễ cơ bản có giá trị
Nếu bạn muốn chức năng của mình tùy chọn chuyển một giá trị độ phân giải, trên bất kỳ trình duyệt mơ hồ hiện đại nào cho phép bạn cung cấp thêm các đối số setTimeout
sau khoảng thời gian trì hoãn và sau đó chuyển các đối số đó đến lệnh gọi lại khi được gọi, bạn có thể làm điều này (Firefox và Chrome hiện tại; IE11 + , có lẽ là Edge; không phải IE8 hoặc IE9, không có ý tưởng về IE10):
function later(delay, value) {
return new Promise(function(resolve) {
setTimeout(resolve, delay, value); // Note the order, `delay` before `value`
/* Or for outdated browsers that don't support doing that:
setTimeout(function() {
resolve(value);
}, delay);
Or alternately:
setTimeout(resolve.bind(null, value), delay);
*/
});
}
Nếu bạn đang sử dụng ES2015 + các hàm mũi tên, có thể ngắn gọn hơn:
function later(delay, value) {
return new Promise(resolve => setTimeout(resolve, delay, value));
}
hoặc thậm chí
const later = (delay, value) =>
new Promise(resolve => setTimeout(resolve, delay, value));
Trì hoãn có thể hủy bỏ với giá trị
Nếu bạn muốn có thể hủy thời gian chờ, bạn không thể chỉ trả lại lời hứa từ đó later
, vì không thể hủy bỏ lời hứa.
Nhưng chúng ta có thể dễ dàng trả về một đối tượng với một cancel
phương thức và một trình truy cập cho lời hứa và từ chối lời hứa khi hủy bỏ:
const later = (delay, value) => {
let timer = 0;
let reject = null;
const promise = new Promise((resolve, _reject) => {
reject = _reject;
timer = setTimeout(resolve, delay, value);
});
return {
get promise() { return promise; },
cancel() {
if (timer) {
clearTimeout(timer);
timer = 0;
reject();
reject = null;
}
}
};
};
Ví dụ trực tiếp:
const later = (delay, value) => {
let timer = 0;
let reject = null;
const promise = new Promise((resolve, _reject) => {
reject = _reject;
timer = setTimeout(resolve, delay, value);
});
return {
get promise() { return promise; },
cancel() {
if (timer) {
clearTimeout(timer);
timer = 0;
reject();
reject = null;
}
}
};
};
const l1 = later(100, "l1");
l1.promise
.then(msg => { console.log(msg); })
.catch(() => { console.log("l1 cancelled"); });
const l2 = later(200, "l2");
l2.promise
.then(msg => { console.log(msg); })
.catch(() => { console.log("l2 cancelled"); });
setTimeout(() => {
l2.cancel();
}, 150);
Câu trả lời gốc từ năm 2014
Thông thường, bạn sẽ có một thư viện lời hứa (một thư do bạn tự viết, hoặc một trong một số thư viện có sẵn). Thư viện đó thường sẽ có một đối tượng mà bạn có thể tạo và sau đó "giải quyết", và đối tượng đó sẽ có một "lời hứa" mà bạn có thể nhận được từ nó.
Sau đó later
sẽ có xu hướng trông giống như sau:
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
Trong một bình luận về câu hỏi, tôi đã hỏi:
Bạn đang cố gắng tạo thư viện lời hứa của riêng mình?
và bạn nói
Tôi thì không nhưng tôi đoán bây giờ đó thực sự là những gì tôi đang cố gắng hiểu. Đó là cách một thư viện sẽ làm điều đó
Để hỗ trợ sự hiểu biết đó, đây là một ví dụ rất cơ bản , không tuân thủ Promises-A từ xa: Live Copy
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Very basic promises</title>
</head>
<body>
<script>
(function() {
// ==== Very basic promise implementation, not remotely Promises-A compliant, just a very basic example
var PromiseThingy = (function() {
// Internal - trigger a callback
function triggerCallback(callback, promise) {
try {
callback(promise.resolvedValue);
}
catch (e) {
}
}
// The internal promise constructor, we don't share this
function Promise() {
this.callbacks = [];
}
// Register a 'then' callback
Promise.prototype.then = function(callback) {
var thispromise = this;
if (!this.resolved) {
// Not resolved yet, remember the callback
this.callbacks.push(callback);
}
else {
// Resolved; trigger callback right away, but always async
setTimeout(function() {
triggerCallback(callback, thispromise);
}, 0);
}
return this;
};
// Our public constructor for PromiseThingys
function PromiseThingy() {
this.p = new Promise();
}
// Resolve our underlying promise
PromiseThingy.prototype.resolve = function(value) {
var n;
if (!this.p.resolved) {
this.p.resolved = true;
this.p.resolvedValue = value;
for (n = 0; n < this.p.callbacks.length; ++n) {
triggerCallback(this.p.callbacks[n], this.p);
}
}
};
// Get our underlying promise
PromiseThingy.prototype.promise = function() {
return this.p;
};
// Export public
return PromiseThingy;
})();
// ==== Using it
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise(); // Note we're not returning `p` directly
}
display("Start " + Date.now());
later().then(function() {
display("Done1 " + Date.now());
}).then(function() {
display("Done2 " + Date.now());
});
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
})();
</script>
</body>
</html>