Подтвердить что ты не робот

Как сделать обещание от setTimeout

Это не проблема реального мира, я просто пытаюсь понять, как создаются promises.

Мне нужно понять, как сделать обещание для функции, которая ничего не возвращает, например setTimeout.

Предположим, что у меня есть:

function async(callback){ 
    setTimeout(function(){
        callback();
    }, 5000);
}

async(function(){
    console.log('async called back');
});

Как создать обещание, которое async может вернуть после того, как setTimeout готов к callback()?

Я предполагал, что это будет куда-то:

function setTimeoutReturnPromise(){

    function promise(){}

    promise.prototype.then = function() {
        console.log('timed out');
    };

    setTimeout(function(){
        return ???
    },2000);


    return promise;
}

Но я не могу думать об этом.

4b9b3361

Ответ 1

Обновление (2017)

Здесь, в 2017 году, Promises встроены в JavaScript, они были добавлены спецификацией ES2015 (полисы доступны для устаревших сред, таких как IE8-IE11). Синтаксис, который они использовали, использует обратный вызов, который вы передаете в конструктор Promise (исполнитель Promise), который получает функции для разрешения/отклонения обещания в качестве аргументов.

Во-первых, поскольку async теперь имеет смысл в JavaScript (хотя это только ключевое слово в определенных контекстах), я будет использовать later как имя функции, чтобы избежать путаницы.

Базовая задержка

Используя native Promises (или верный polyfill), он будет выглядеть так:

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); // 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);
        */
    });
}

Если вы используете функции стрелок 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, потому что Promises не может быть отменено.

Но мы можем легко вернуть объект с помощью метода 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);