而使用Promise的循环卡在无限循环中

时间:2018-01-25 17:43:54

标签: javascript

我正在运行一个while循环,其中包含一个包含成功和失败回调的Promise。 Promise检查图像是否成功加载到URL。

查看下面的代码,我会不断循环查看1.jpg2.jpg的命名照片列表,等等example.com,每次发送一份承诺。

然而,循环陷入无限循环。当我运行下面的代码时,我的浏览器没有响应。

为什么会这样?

另外,为什么在Promise失败回调中使用break;时会收到非法的break语句?

function photoCheck(url) {
    return new Promise((resolve, reject) => {
        let img = document.createElement('img')
        img.onload = resolve
        img.onerror = reject
        img.src = url
    })
}

photoCount = 0;
while (running == true) {
    photoCount += 1;
    photo = 'example.com/' + photoCount + '.jpg';
    photoCheck(photo).then(() => {
        var img = document.createElement('img');
        img.src = photo;
        body.appendChild(img);
    }, () => {
        running = false
        // If I use `break;`, I get an illegal break statement error.
    }) 
}

2 个答案:

答案 0 :(得分:2)

您似乎无法理解异步JavaScript代码和JavaScript事件循环的工作原理。我建议阅读this book的前三章,以便更好地理解这些概念。

没有可能的方法,你在那里可以工作,因为running在循环结束之前不可能更改为false(并且它永远不会)。只要任何阻止代码正在运行,then回调就无法执行。

一种可能的替代方法是使用递归逻辑在检索每个照片后获取下一张照片。这样做的一个缺点是它一次只能获取一张照片,但好处是你最多只能有一张失败的请求:

function photoCheck(url) {
    return new Promise((resolve, reject) => {
        let img = document.createElement('img');
        img.onload = () => resolve(img);
        img.onerror = reject;
        img.src = url;
    })
}

function getPhoto(num) {
    const photo = 'example.com/' + num + '.jpg';

    return photoCheck(photo)
         .then((img) => {
             body.appendChild(img);

             return getPhoto(num + 1);
         });
}

getPhoto(1);

OTOH,因为看起来你正在使用现代版本的JavaScript,你可以在async函数中使用循环:

function photoCheck(url) {
    return new Promise((resolve, reject) => {
        let img = document.createElement('img');
        img.onload = () => resolve(img);
        img.onerror = reject;
        img.src = url;
    })
}

async function getPhotos() {
    try {
        for (let photoNum = 1; ; photoNum += 1) {
            const photo = 'example.com/' + photoNum + '.jpg';

            body.appendChild(await photoCheck(photo));
        }
    } catch(e) { }
}

getPhotos().then(() => console.log('all done'));
  

另外,为什么我在使用break时会收到非法的break语句;在Promise失败回调?

因为break;语句必须是循环中的一个语句。您正试图将break;放入单独的函数中,它不会成为任何循环的一部分。

答案 1 :(得分:1)

试试这个:

function photoCheck(url) {
    return new Promise((resolve, reject) => {
        let img = document.createElement('img')
        img.onload = resolve
        img.onerror = reject
        img.src = url
    })
}

function loadPhotos(current) {
    var photo = 'example.com/' + current + '.jpg';
    photoCheck(photo).then(() => {
        var img = document.createElement('img');
        img.src = photo;
        body.appendChild(img);
        loadPhotos(current + 1);
    }, () => {}) 
}

loadPhotos(1);

您的代码存在的问题是承诺'解决回调从来没有机会执行,因为你的while循环仍然在运行,在JavaScript中同时运行两件事(除非你使用工人)。

在处理异步代码时,这是一个非常重要的概念。

另一种选择是使用async / await:

function photoCheck(url) {
    return new Promise((resolve, reject) => {
        let img = document.createElement('img');
        img.onload = () => resolve(img);
        img.onerror = reject;
        img.src = url;
    })
}

(async function () {
    var photoCount = 0;
    while (true) {
        photoCount++;
        var photo = 'example.com/' + photoCount + '.jpg';

        try {
            var img = await photoCheck(photo);
            document.body.appendChild(img);
        } catch(e) {
            break;
        }
    }   
})();

但我建议您在使用async / await之前尝试更深入地了解异步调用的工作原理。