Javascript 通过创建一个新承诺然后运行和创建所有承诺然后运行每个承诺的运行序列承诺之间的差异

Javascript 通过创建一个新承诺然后运行和创建所有承诺然后运行每个承诺的运行序列承诺之间的差异,javascript,asynchronous,promise,sequence,Javascript,Asynchronous,Promise,Sequence,请帮助我解释为什么日志结果在两个方面不同: 方式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(c

请帮助我解释为什么日志结果在两个方面不同:

方式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”(每个计时器间隔1秒)排序,并以“方式2”并行运行计时器,因此计时器几乎同时启动。下面是更详细的解释:

在“方式1”中,您创建了一个带有
sequence=sequence的承诺链。然后()
从序列中调用
setTimeout()
。因此,计时器2在计时器1触发后才会启动,以此类推。您将使每个计时器真正按顺序运行,每次触发间隔约为1秒

在“方式2”中,您可以在
.map()
中同时启动所有计时器,因此所有计时器都是并行运行的,而不是按顺序运行的。然后,您尝试使用
sequence2=sequence2.then()
循环将它们强制成一个序列,但异步操作已经并行启动,因此所有这些顺序循环实际上只完成了一个
承诺。all()
在计时方面的作用

如果您运行这两个代码段中的每一个,它们将准确地记录计时器启动的时间,您可以看到两者之间的差异

这里,在记录每个计时器时间序列的“方式1”版本中,您可以看到计时器相隔约1秒触发:

//方法1
让startTime=Date.now();
函数日志(){
设args=Array.from(参数);
//自启动时间起的计算时间
设delta=(Date.now()-startTime)/1000;
参数反移位(增量+“:”);
console.log.apply(console,args);
}
功能延迟(t,val){
返回新承诺(解决=>{
设置超时(()=>{
日志(“计时器火灾”);
解决(val);
},t);
});
}
//方式1
让序列=Promise.resolve();
[1,2,3,4]。forEach((val)=>{
顺序=顺序。然后(()=>{
返回延迟(1000,val);
}).然后(console.log);
});
顺序。然后(()=>{
日志(“全部完成”);

})
要理解这一点,你需要理解承诺只是等待解决,所以在解决之前,以下(.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调用,因此一秒钟后,所有承诺都已解决。所以(.then)不需要等待

 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); 

因此,尽管它们看起来与第一个语法相似,但承诺都已解决,因此无需等待。

您的映射函数是同步的,因此可以当场执行所有操作。这里我所说的执行,只是将承诺添加到未来的调用堆栈中,然后继续,因为承诺是异步的。“顺序循环实际上只完成了一个
承诺。all()
将”-它们的行为在实现值和拒绝处理方式之间有所不同。我认为顺序循环是错误的:-)@Bergi-我只是指时间。显然,其他一切都不同。编辑以澄清。我真的不明白为什么.map()并行运行整个承诺(请参见@Shyam Babu的答案)@windluffy-
。map()是一个同步函数。您创建的承诺是非阻塞的,因此它们只是创建了承诺并立即返回,然后在一段时间后解决。因此,您可以立即启动所有计时器,并最终得到一系列表示已启动的异步操作的承诺。我不明白你不明白的部分。您知道
setTimeout()
是非阻塞的,对吗?它为将来的某个时间安排计时器,然后立即返回。在方法2中,我确实尝试“使用sequence2=sequence2.then()循环将它们强制成一个序列”,但我仍然不理解为什么“异步操作已经并行启动”尽管您已经转换了代码片段,但我不理解为什么“一次推送所有setimout调用”。我看到settimeout在单独的承诺中被调用