运行序列之间的差异是通过创建新的承诺,然后运行并创建所有承诺,然后运行每个承诺



请帮助我解释为什么日志结果在两种方式上不同:

方式1:依次每1秒记录

方式2:1秒后所有元素都被记录。

// Way 1
let sequence = Promise.resolve();
[1,2,3,4].forEach((val)=> {
    sequence = sequence.then(()=> {
        return new Promise(resolve => setTimeout(resolve, 1000, val))
    }).then(console.log);
})

//Way 2
let sequence2 = Promise.resolve();
[5,6,7,8].map(val => {
    return new Promise(resolve => setTimeout(resolve, 1000, val));
}).forEach(promise => {
    sequence2 = sequence2.then(() => promise).then(console.log);
});

编辑:描述错误地记录结果2

您已经按照"方式1"(他们相距一秒钟开火)对计时器进行了测序,并在"方式2"中并行运行计时器,因此计时器大约在同一时间发射。这是更详细的说明:

在" Way 1"中,您可以使用sequence = sequence.then()创建一个承诺链,并且setTimeout()从序列中调用。因此,直到计时器1射击之后,计时器2才开始。您将使每个计时器都真正顺序运行,每个计时器相距约1秒。

在" Way 2"中,您可以在.map()中立即启动所有计时器,因此所有计时器并行运行,而不是按顺序运行。然后,您正在尝试将它们迫使它们与sequence2 = sequence2.then()循环迫使它们成一个序列,但是异步操作已经并行启动,因此所有这些顺序循环实际上并没有在Promise.all()就时间安排上完成任何其他工作。

如果运行这两个片段中的每一个,它们将在计时器射击时准确登录,并且您可以看到两者之间的区别。

在这里,在记录每个计时器的时间顺序的" Way 1"版本中,您可以看到计时器射击约1秒:

// way 1
let startTime = Date.now();
function log() {
    let args = Array.from(arguments);
    // calc time since startTime
    let delta = (Date.now() - startTime) / 1000;
    args.unshift(delta + ": ");
    console.log.apply(console, args);
}
function delay(t, val) {
    return new Promise(resolve => {
        setTimeout(() => {
            log("timer fire");
            resolve(val);           
        }, t);
    });
}
// Way 1
let sequence = Promise.resolve();
[1,2,3,4].forEach((val)=> {
    sequence = sequence.then(()=> {
        return delay(1000, val);
    }).then(console.log);
});
sequence.then(() => {
    log("all done");
})

在这里,在记录每个计时器的时间顺序的" Way 2"版本中,您可以在同一时间看到计时器发射:

// way 2
let startTime = Date.now();
function log() {
    let args = Array.from(arguments);
    // calc time since startTime
    let delta = (Date.now() - startTime) / 1000;
    args.unshift(delta + ": ");
    console.log.apply(console, args);
}
function delay(t, val) {
    return new Promise(resolve => {
        setTimeout(() => {
            log("timer fire");
            resolve(val);           
        }, t);
    });
}
//Way 2
let sequence2 = Promise.resolve();
[5,6,7,8].map(val => {
    return new delay(1000, val);
}).forEach(promise => {
    sequence2 = sequence2.then(() => promise).then(console.log);
});
sequence2.then(() => {
    log("all done");
});

运行每个片段时,不是特别是到达"全部完成"消息所需的时间。第一个序列序列的四个第二个计时器,因此需要大约4秒钟。第二个并行运行所有计时器,因此运行大约需要1秒钟。


其他说明:

当您这样做时:

let arrayOfPromoises = [5,6,7,8].map(val => {
    return new Promise(resolve => setTimeout(resolve, 1000, val));
});

该代码连续四次执行return new Promise(resolve => setTimeout(resolve, 1000, val));,另一个又一次没有延迟。由于每次执行代码时,它都会创建一个新的Promise对象,最终都会有四个承诺的数组。

现在,在该代码中,您有:

new Promise(resolve => setTimeout(resolve, 1000, val));

Promise Executor函数(这就是您将回调称为您传递给Promise构造函数的内容)。没有等待。因此,您不仅创造了四个诺言,而且您还开始了四次一次运行。这些计时器已经开始。使用sequence2 = sequence2.then()循环没有任何结构化可以改变这一点。计时器已经在运行。

要理解这一点,您需要了解一个承诺只是等待解决方案,因此直到解决方案得到以下(然后)代码链不会执行

对于第一个,我们使用不同的变量而不是同一序列变量,则代码变为。

var firstSequence = Promise.resolve()
                        .then(()=> {return new Promise(resolve => setTimeout(resolve, 1000, 1))})
                        .then(console.log);
var secondSequence = firstSequence
                        .then(()=> {return new Promise(resolve => setTimeout(resolve, 1000, 2))})
                        .then(console.log);
var thirdSequence = secondSequence
                        .then(()=> {return new Promise(resolve => setTimeout(resolve, 1000, 3))})
                        .then(console.log);
var foruthSequence = thirdSequence
                        .then(()=> {return new Promise(resolve => setTimeout(resolve, 1000, 4))})
                        .then(console.log);                       

从中您可以看到,在解决承诺之前,不会发生超时呼叫。因此,它们似乎是顺序的,他们一直被迫等待一秒钟。

,但第二种形式等同于此。

function makePromise(val) {
    return new Promise(resolve => setTimeout(resolve, 1000, val)); 
}
var storageArray  = [makePromise(1), makePromise(2), makePromise(3), 
makePromise(4)];

我们已经将所有的setimout呼叫都拼在一起,因此一秒钟后,所有承诺已经解决。因此(然后)不需要等待。

 var firstSequence = Promise.resolve()
                    .then(()=> {return storageArray[0]})
                    .then(console.log);
 var secondSequence = firstSequence
                    .then(()=> {return storageArray[1]})
                    .then(console.log);
 var thirdSequence = secondSequence
                    .then(()=> {return storageArray[2]})
                    .then(console.log);
 var foruthSequence = thirdSequence
                    .then(()=> {return storageArray[3]})
                    .then(console.log); 

因此,即使它们似乎与第一个语法相似,诺言都是已经解决的,因此不需要等待。

最新更新