Ответ 1
Для тех, кто ищет ответ на эту проблему. Это было признано командой NodeJs как ошибка, более подробно здесь: https://github.com/nodejs/node/issues/22257
Между тем это было уже исправлено и выпущено, имеет часть Node v11.
Бест, Хосе
Я уже давно знаком с концепциями Microtasks и Macrotasks, и из всего, что я читал, я всегда считал, что setTimeout
следует рассматривать как создание макрозадачи и Promise.resolve()
(или process.nextTick
на NodeJS) для создания микротоков.
(Да, я знаю, что разные библиотеки Promise, такие как Q и Bluebird, имеют разные реализации планировщиков, но здесь я имею в виду собственные обещания на каждой платформе)
Имея это в виду, я не могу объяснить следующую последовательность событий в NodeJS (результаты в Chrome отличаются от NodeJS (как v8 LTS, так и v10) и соответствуют моему пониманию по этому вопросу).
for (let i = 0; i < 2; i++) {
setTimeout(() => {
console.log("Timeout ", i);
Promise.resolve().then(() => {
console.log("Promise 1 ", i);
}).then(() => {
console.log("Promise 2 ", i);
});
})
}
Для тех, кто ищет ответ на эту проблему. Это было признано командой NodeJs как ошибка, более подробно здесь: https://github.com/nodejs/node/issues/22257
Между тем это было уже исправлено и выпущено, имеет часть Node v11.
Бест, Хосе
Вы не можете контролировать, как разные архитектуры ставят в очередь обещания и тайм-ауты.
Отлично читайте здесь: https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
Если вы хотите получить те же результаты, вам придется перекладывать обещания.
let chain = Promise.resolve(null)
for (let i = 0; i < 2; i++) {
console.log("Chaining ", i);
chain = chain.then(() => Promise.resolve()
.then(() => {
setTimeout(() => {
console.log("Timeout ", i);
Promise.resolve()
.then(() => {
console.log("Promise 1 ", i);
})
.then(() => {
console.log("Promise 2 ", i);
})
}, 0)
}))
}
chain.then(() => console.log('done'))
Я не говорю, что все правильно, я написал что-то adhoc, и я бы хотел, чтобы вы протестировали следующее:
обертка:
function order(){
this.tasks = [];
this.done = false;
this.currentIndex = 0;
this.ignited = false;
}
order.prototype.push = function(f){
var that = this,
args = Array.prototype.slice.call(arguments).slice(1);
if(this._currentCaller){
this.tasks.splice(
this.tasks.indexOf(this._currentCaller) + 1 + (this.currentIndex++),
0,
function(){that._currentCaller = f; f.apply(this,args);}
);
} else {
this.tasks.push(function(){that._currentCaller = f; f.apply(this,args);});
}
!this.ignited && (this.ignited = true) && this.ignite();
return this;
}
order.prototype.ignite = function(){
var that = this;
setTimeout(function(){
if(that.tasks.length){
that.tasks[0]();
that.tasks.shift();
that.repeat(function(){that.reset(); that.ignite()});
} else {
that.ignited = false;
that.reset();
}
},0);
}
order.prototype.repeat = function(f){
var that = this;
if(this.done || !this.tasks.length){
f();
} else {
setTimeout(function(){that.repeat(f);},0);
}
}
order.prototype.reset = function(){
this.currentIndex = 0;
delete this._currentCaller;
this.done = false;
}
использовать:
создать экземпляр:
var x = new order;
затем немного измените остальные:
for (let i = 0; i < 2; i++) {
x.push(function(i){
setTimeout(() => {
console.log("Timeout ", i);
x.push(function(i){
Promise.resolve().then(() => {
console.log("Promise 1 ", i);
}).then(() => {
console.log("Promise 2 ", i);
x.done = true;
})
},i);
x.done = true;
});
},i);
}
Я получаю это:
Timeout 0
Promise 1 0
Promise 2 0
Timeout 1
Promise 1 1
Promise 2 1
Вы можете даже немного доработать:
for (let i = 0; i < 2; i++) {
x.push(function(i){
setTimeout(() => {
console.log("Timeout ", i);
x.push(function(i){
Promise.resolve().then(() => {
console.log("Promise 1 ", i);
}).then(() => {
console.log("Promise 2 ", i);
x.done = true;
})
},i)
.push(function(i){
Promise.resolve().then(() => {
console.log("Promise 1 ", i);
}).then(() => {
console.log("Promise 2 ", i);
x.done = true;
})
},i+0.5)
.push(function(i){
Promise.resolve().then(() => {
console.log("Promise 1 ", i);
}).then(() => {
console.log("Promise 2 ", i);
x.done = true;
})
},i+0.75);
x.done = true;
});
},i);
}
В узле v6 вы получаете:
Timeout 0
Promise 1 0
Promise 2 0
Promise 1 0.5
Promise 2 0.5
Promise 1 0.75
Promise 2 0.75
Timeout 1
Promise 1 1
Promise 2 1
Promise 1 1.5
Promise 2 1.5
Promise 1 1.75
Promise 2 1.75
Вы бы попробовали это в своей версии для меня? В моем узле (6.11, я знаю его старый) он работает.
Протестировано на chrome, firefox, node v6.11
Примечание: вам не нужно ссылаться на "x", this
пределах нажатых функций относится к экземпляру order
. Вы также можете использовать Object.defineProperties
для рендеринга Object.defineProperties
/Object.defineProperties
, чтобы предотвратить случайное удаление instance.ignited
и т.д.