Использование setTimeout в цепочке обещаний

Здесь я пытаюсь обернуть голову вокруг обещаний. Здесь при первом запросе я получаю набор ссылок. И при следующем запросе я получаю содержимое первой ссылки. Но я хочу сделать задержку перед возвратом следующего объекта обещания. Так что я использую 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);
});

8 ответов

Решение

Чтобы сохранить цепочку обещаний, вы не можете использовать 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() обработчик и, таким образом, он соответствующим образом прикован.


Вы также можете добавить метод задержки к объекту Promise, а затем напрямую использовать .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() метод встроенный.

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

Короче версия ES6 ответа:

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

И тогда вы можете сделать:

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

Если вы находитесь внутри блока .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!!!

Удачного кодирования!

Начиная с узла v15, вы можете использовать API обещаний таймеров

пример из документа:

      import { setTimeout } from 'timers/promises'

const res = await setTimeout(100, 'result')

console.log(res)  // Prints 'result'

В node.js вы также можете делать следующее:

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

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

Для текущего LTS это проще, и мы можем использовать async/await для обработки тайм-аутов. Обратите внимание, что в настоящее время это рекомендуемый способ использования тайм-аута.

Thenables не рекомендуется.

      const { promisify } = require('util')
const sleep = promisify(setTimeout)
async function myFunction() {
  await sleep(1e3)
  console.log('This will be seen after 1 sec')
  await sleep(5e3)
  console.log('This will be seen after 5 sec after')
}
      const myStuff = new Promise(function (resolve) {
  console.log("before timeout");
  setTimeout(
    function (x) {
      console.log("inside the timeout");
      resolve(x);
    },
    3000,
    "after timeout"
  );
}).then((response) => console.log(response));
Другие вопросы по тегам