Использование 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));