за допомогою setTimeout в ланцюжку обіцянок


115

Ось я намагаюся обернути голову навколо обіцянок. Ось за першим запитом я отримую набір посилань. І на наступний запит я вибираю вміст першої посилання. Але я хочу зробити затримку перед поверненням наступного об'єкта обіцянки. Тому я використовую setTimeout на ньому. Але це дає мені таку помилку JSON ( without setTimeout() it works just fine)

SyntaxError: JSON.parse: несподіваний символ у колонці 1 рядка 1 даних JSON

я хотів би знати, чому це не вдається?

let globalObj={};
function getLinks(url){
    return new Promise(function(resolve,reject){

       let http = new XMLHttpRequest();
       http.onreadystatechange = function(){
            if(http.readyState == 4){
              if(http.status == 200){
                resolve(http.response);
              }else{
                reject(new Error());
              }
            }           
       }
       http.open("GET",url,true);
       http.send();
    });
}

getLinks('links.txt').then(function(links){
    let all_links = (JSON.parse(links));
    globalObj=all_links;

    return getLinks(globalObj["one"]+".txt");

}).then(function(topic){


    writeToBody(topic);
    setTimeout(function(){
         return getLinks(globalObj["two"]+".txt"); // without setTimeout it works fine 
         },1000);
});

1
Зауважте, що returnце функція, яка повертається лише до батьківської функції, і що ви не можете повернутися з методу асинхронізації.
adeneo

2
Зауважте, є набагато кращі способи структурування цього коду, ніж використання globalObj.
Бергі

Куди JSON.parseкидає? Мені важко повірити, що наявність зворотного дзвінка setTimeoutв одному thenвпливає на виклик у попередньому thenзворотному дзвінку .
Бергі

Відповіді:


191

Щоб продовжувати ланцюжок обіцянок, ви не можете використовувати setTimeout()так, як це зробили, оскільки ви не повертаєте обіцянку від .then()обробника - ви повертаєте її зsetTimeout() зворотного дзвінка, що не приносить вам користі.

Натомість ви можете зробити просту функцію невеликої затримки на зразок цієї:

function delay(t, v) {
   return new Promise(function(resolve) { 
       setTimeout(resolve.bind(null, v), t)
   });
}

А потім використовуйте його так:

getLinks('links.txt').then(function(links){
    let all_links = (JSON.parse(links));
    globalObj=all_links;

    return getLinks(globalObj["one"]+".txt");

}).then(function(topic){
    writeToBody(topic);
    // return a promise here that will be chained to prior promise
    return delay(1000).then(function() {
        return getLinks(globalObj["two"]+".txt");
    });
});

Тут ви повертаєте обіцянку від .then()обробника, і, таким чином, це приковується відповідним чином.


Ви також можете додати метод затримки до об’єкту Обіцяти, а потім безпосередньо використовувати .delay(x)метод для своїх обіцянок:

function delay(t, v) {
   return new Promise(function(resolve) { 
       setTimeout(resolve.bind(null, v), t)
   });
}

Promise.prototype.delay = function(t) {
    return this.then(function(v) {
        return delay(t, v);
    });
}


Promise.resolve("hello").delay(500).then(function(v) {
    console.log(v);
});

Або скористайтеся бібліотекою обіцянок Bluebird, яка вже має .delay()вбудований метод.


1
функція вирішення - це функція всередині тоді () .. тому setTimeout (рішення, t) означає setTimeout (функція () {повернення ....}, т) чи не так ... чому він буде працювати?
AL-zami

2
@ AL-zami - delay()повертає обіцянку, яка буде вирішена після setTimeout().
jfriend00

Я створив обгортку обіцянок для setTimeout, щоб легко відкласти обіцянку. github.com/zengfenfei/delay
Кевін

4
@pdem - vце необов'язкове значення, яке ви хочете вирішити із затримкою обіцянки і таким чином передати ланцюжок обіцянок. resolve.bind(null, v)є на місці, function() {resolve(v);} або буде працювати.
jfriend00

велике спасибі ... затримка прототипу спрацювала, але не функція >>> .then заява. t не визначено.
Крістіан Матвій

76
.then(() => new Promise((resolve) => setTimeout(resolve, 15000)))

ОНОВЛЕННЯ:

коли мені потрібен сон у функції асинхронізації, яку я кидаю

await new Promise(resolve => setTimeout(resolve, 1000))

Не могли б ви просто спати в такій функції асинхронізації, як це? чекайте нової Обіцянки (вирішити => setTimeout (вирішити, 1000));
Anthony Moon Beam Toorie

@AnthonyMoonBeamToorie виправлено, ty
Ігор Корсаков

52

Більш коротка версія відповіді ES6:

const delay = t => new Promise(resolve => setTimeout(resolve, t));

І тоді ви можете зробити:

delay(3000).then(() => console.log('Hello'));

а якщо вам потрібен rejectваріант, наприклад, для перевірки еслінт, тоconst delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
Девід Томас

10

Якщо ви перебуваєте всередині .then () блоку , і ви хочете виконати SetTimeout ()

            .then(() => {
                console.log('wait for 10 seconds . . . . ');
                return new Promise(function(resolve, reject) { 
                    setTimeout(() => {
                        console.log('10 seconds Timer expired!!!');
                        resolve();
                    }, 10000)
                });
            })
            .then(() => {
                console.log('promise resolved!!!');

            })

Вихід буде, як показано нижче

wait for 10 seconds . . . .
10 seconds Timer expired!!!
promise resolved!!!

Щасливе кодування!


-1

У node.js ви також можете зробити наступне:

const { promisify } = require('util')
const delay = promisify(setTimeout)

delay(1000).then(() => console.log('hello'))

Я спробував це і отримав недійсну кількість аргументів, очікуваний 0 у функції затримки.
Алекс Ріндоне

Я можу підтвердити, що він працює в node.js 8, 10, 12, 13. Не впевнений, як ви запускаєте свій код, але я можу лише припустити util, що він заповнюється неправильно. Ви використовуєте постачальник або щось таке?
Jan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.