Оновлення (2017)
Ось у 2017 році Promises вбудовані в JavaScript, вони були додані специфікацією ES2015 (полізаповнення доступні для застарілих середовищ, таких як IE8-IE11). Синтаксис, з яким вони пішли, використовує зворотний виклик, який ви передаєте в Promise
конструктор ( Promise
виконавець ), який отримує функції для вирішення / відхилення обіцянки як аргументи.
По-перше, оскільки async
зараз значення має значення в JavaScript (хоча це лише ключове слово у певному контексті), я буду використовувати його later
як назву функції, щоб уникнути плутанини.
Основна затримка
Використовуючи рідні обіцянки (або вірний поліфіл), це буде виглядати так:
function later(delay) {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
}
Зверніть увагу, що передбачається версія, setTimeout
яка відповідає визначенню для браузерів, де setTimeout
не передаються аргументи зворотного виклику, якщо ви не надаєте їх після інтервалу (це може бути неправдою в не браузерних середовищах і раніше не було правда для Firefox, але зараз; це справедливо для Chrome і навіть для IE8).
Базова затримка зі значенням
Якщо ви хочете, щоб ваша функція необов’язково передавала значення роздільної здатності, у будь-якому неясно сучасному браузері, який дозволяє вам надати додаткові аргументи setTimeout
після затримки, а потім передає їх до зворотного виклику при виклику, ви можете це зробити (поточні Firefox та Chrome; IE11 + , імовірно Edge; не IE8 або IE9, не маю уявлення про IE10):
function later(delay, value) {
return new Promise(function(resolve) {
setTimeout(resolve, delay, value);
});
}
Якщо ви використовуєте функції ES2015 + зі стрілками, це може бути більш стисло:
function later(delay, value) {
return new Promise(resolve => setTimeout(resolve, delay, value));
}
або навіть
const later = (delay, value) =>
new Promise(resolve => setTimeout(resolve, delay, value));
Скасовувана затримка зі значенням
Якщо ви хочете зробити можливим скасувати час очікування, ви не можете просто повернути обіцянку від later
, оскільки обіцянки не можна скасувати.
Але ми можемо легко повернути об'єкт із cancel
методом та засобом доступу до обіцянки та відхилити обіцянку після скасування:
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 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);
Оригінальна відповідь від 2014 року
Зазвичай у вас є бібліотека обіцянок (та, яку ви пишете самі, або одна з кількох там). У цій бібліотеці, як правило, є об’єкт, який ви можете створити, а згодом «вирішити», і цей об’єкт матиме «обіцянку», яку ви можете отримати від нього.
Тоді later
, як правило, виглядатиме приблизно так:
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise();
}
У коментарі до запитання я запитав:
Ви намагаєтесь створити власну бібліотеку обіцянок?
і ти сказав
Я не був, але, мабуть, зараз саме це я і намагався зрозуміти. Це те, як це зробила б бібліотека
Щоб допомогти цьому зрозуміти, ось дуже простий приклад, який не віддалено відповідає Promises-A: Live Copy
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Very basic promises</title>
</head>
<body>
<script>
(function() {
var PromiseThingy = (function() {
function triggerCallback(callback, promise) {
try {
callback(promise.resolvedValue);
}
catch (e) {
}
}
function Promise() {
this.callbacks = [];
}
Promise.prototype.then = function(callback) {
var thispromise = this;
if (!this.resolved) {
this.callbacks.push(callback);
}
else {
setTimeout(function() {
triggerCallback(callback, thispromise);
}, 0);
}
return this;
};
function PromiseThingy() {
this.p = new 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);
}
}
};
PromiseThingy.prototype.promise = function() {
return this.p;
};
return PromiseThingy;
})();
function later() {
var p = new PromiseThingy();
setTimeout(function() {
p.resolve();
}, 2000);
return p.promise();
}
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>