运行顺序承诺之间的区别是通过创建新的承诺然后运行并创建所有承诺然后运行每个承诺

时间:2017-09-06 14:46:55

标签: 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(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中错误地记录日志结果

2 个答案:

答案 0 :(得分:1)

您已按照"方式1"对您的计时器进行了排序(它们各自相隔一秒)然后并行地运行你的计时器"方式2"所以计时器大约在同一时间点火。这里有更详细的解释:

在"方式1"中,您使用sequence = sequence.then()创建一系列承诺,并从序列中调用setTimeout()。因此,计时器2不会启动,直到计时器1发生之后,依此类推。你将使每个单独的计时器真正顺序运行,每次点火约1秒钟。

在"方式2"中,您在.map()中同时启动所有计时器,因此所有计时器并行运行,而不是按顺序运行。然后,您尝试使用sequence2 = sequence2.then()循环强制它们进入序列,但是异步操作已经并行启动,因此所有这些顺序循环并不能真正完成Promise.all()以外的任何操作。在时间方面。

如果您运行这两个片段中的每一个,它们将在计时器触发时准确记录,您可以看到两者之间的差异。

这里,在"方式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");
})




这里,在"方式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对象,最终会得到一个包含四个promise的数组。

现在,在该代码中你有这个:

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

Promise执行程序函数(即传递给Promise构造函数的回调函数)被立即调用。没有等待。所以,你不仅创造了四个承诺,而且你也开始了四次同时运行。这些计时器已经开始了。使用sequence2 = sequence2.then()循环进行任何数量的结构都不会改变这种情况。计时器已经在运行。

答案 1 :(得分:0)

要理解这一点,你需要明白承诺只是等待解决的事情,所以在得到解决方案之前,以下(.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);                       
从这里可以看出,在解析promise之前不会发生超时调用。所以它们似乎是连续的,并且它们不断被推迟等待一秒钟。

但第二种形式相当于此。

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

所以尽管它们似乎与第一个语法类似,但承诺都已经解决,因此无需等待。