Javascript 而有承诺的循环被困在无限循环中

Javascript 而有承诺的循环被困在无限循环中,javascript,Javascript,我正在运行一个while循环,其中包含了成功和失败回调的承诺。承诺检查图像是否成功加载到URL 查看下面的代码,我不断循环查看example.com的命名照片列表1.jpg,2.jpg,等等,每次都发送一个承诺 然而,循环被卡在无限循环中。当我运行下面的代码时,我的浏览器没有响应 为什么会这样 还有,为什么我使用break时会得到非法的break语句 function photoCheck(url) { return new Promise((resolve, reject) =>

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

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

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

为什么会这样

还有,为什么我使用
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.
    }) 
}
试试这个:

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);
代码的问题是promises的resolve回调永远不会执行,因为while循环仍在运行,JavaScript中不能同时运行两个东西(除非使用workers)

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

另一个选项是使用异步/等待:

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之前,尝试更深入地了解异步调用的工作原理。

尝试以下方法:

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);
代码的问题是promises的resolve回调永远不会执行,因为while循环仍在运行,JavaScript中不能同时运行两个东西(除非使用workers)

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

另一个选项是使用异步/等待:

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之前,尝试更深入地了解异步调用是如何工作的。

您似乎不了解异步JavaScript代码和JavaScript事件循环是如何工作的。我建议阅读的前三章,以更好地理解这些概念

您所拥有的东西不可能起作用,因为在循环完成之前,
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语句;在承诺失败回调中

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.
    }) 
}

因为一个
中断语句必须是循环中的语句之一。您试图将
中断
在一个单独的函数中,它不会成为任何循环的一部分。

您似乎不了解异步JavaScript代码和JavaScript事件循环是如何工作的。我建议阅读的前三章,以更好地理解这些概念

您所拥有的东西不可能起作用,因为在循环完成之前,
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语句;在承诺失败回调中

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.
    }) 
}

因为一个
中断语句必须是循环中的语句之一。您试图将
中断在一个单独的函数中,它不会成为任何循环的一部分。

如果承诺没有被拒绝,无限循环是可能的。因为只有在这种情况下才设置running=false。@cauchy但即使链接断开,这意味着应该调用承诺拒绝回调,循环似乎仍在运行。也许while循环的迭代速度比承诺的解析速度快得多。忽略这里的其他问题,假设您只有10个图像,但您的while循环在图像11最终被拒绝之前生成了1000个承诺。它仍然需要通过并拒绝其他989图像。如果承诺不被拒绝,无限循环是可能的。因为只有在这种情况下才设置running=false。@cauchy但即使链接断开,这意味着应该调用承诺拒绝回调,循环似乎仍在运行。也许while循环的迭代速度比承诺的解析速度快得多。忽略这里的其他问题,假设您只有10个图像,但您的while循环在图像11最终被拒绝之前生成了1000个承诺。它仍然需要通过并拒绝其他989图像。