Оновлення (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>