предисловие
Метод Promise Race — это более простой способ использования, когда мы используем обещания. Согласно определению гонки обещаний MDN следующим образом:
The Promise.race(iterable) method returns a promise that resolves or rejects as soon as one of the promises in the iterable resolves or rejects, with the value or reason from that promise.
Согласно своему буквальному значению, метод Promise.race возвращает обещание, которое является обещанием, которое было разрешено. Его разрешенное значение — это значение самого быстрого обещания, которое было разрешено, или значение, которое было отклонено.
Другими словами, Promise.race должен предоставить массив обещаний в качестве входных данных, а самое быстрое разрешенное значение используется в качестве разрешающего или отклоненного значения возвращаемого значения обещания.
Пример кода, размещенный в MDN, выглядит следующим образом:
var promise1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var promise2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then(function(value) {
console.log(value);
// Both resolve, but promise2 is faster
});
// expected output: "two"
Легко привести к непониманию
В приведенном выше коде есть комментарий «Оба разрешаются, но promise2 быстрее», поэтому ожидаемый результат равен «два». Это создаст у нас иллюзию, что какое бы обещание ни было быстрее, оно должно вернуть значение разрешения. На самом деле здесь есть некоторые предпосылки.
- Promise.race должен быть как можно больше в определенном промисе.Oнипозвони после.
- В некоторых случаях promise2 может не обязательно возвращать свое значение, даже если он быстрее.
Давайте подробно поговорим о двух упомянутых выше ситуациях, которые могут вызвать ошибки Promise.race.
-
Promise.race должен быть как можно больше в определенном промисе.Oнипозвони после.
Мы немного изменили код MDN, чтобы Promise.race не выполнялся сразу, а выполнялся в следующем цикле выполнения.
var promise1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, 'one');
});
var promise2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, 'two');
});
// 在这里,我使用了一个定时器回调 Promise.race 方法。
// 这个定时器的时间正好为两个 promise 所要等待时间的最长时间,即500ms。
// 这时, console.log(value)的值只和第一个 promise 相关联,
// 就算 promise2 比 promise1 快,返回的结果还是 “one”
setTimeout(()=> {
Promise.race([promise1, promise2]).then(function(value) {
console.log(value);
});
}, 500)
-
В некоторых случаях promise2 может не обязательно возвращать свое значение, даже если он быстрее.
Давайте внесем некоторые коррективы в код MDN следующим образом:
var promise1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 1, 'one');
});
var promise2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 0, 'two');
});
Promise.race([promise1, promise2]).then(function(value) {
console.log(value);
// Both resolve, but promise2 is faster
});
Приведенный выше код является более экстремальным, но он также может отражать некоторые вещи. promise2 все еще быстрее, но возвращает «один». (Вероятно, это место связано с механизмом setTimeout. Когда я устанавливаю promise1 больше или равным 2, возвращаемый результат равен «два». Надеюсь, кто-то, кто это знает, объяснит это. Я продолжу изучение соответствующий механизм работы setTimeout в будущем.)
первородный грех
Почему возникает вышеуказанная ошибка? Сначала мы можем взглянуть на исходный код реализации Promise.race.
CDN.JS Deli VR.net/Годовой рейтинг/Starve 6-pro…
function race(entries) {
/*jshint validthis:true */
var Constructor = this; // this 是调用 race 的 Promise 构造器函数。
if (!isArray(entries)) {
return new Constructor(function (_, reject) {
return reject(new TypeError('You must pass an array to race.'));
});
} else {
return new Constructor(function (resolve, reject) {
var length = entries.length;
for (var i = 0; i < length; i++) {
Constructor.resolve(entries[i]).then(resolve, reject);
}
});
}
}
Таким образом, принцип реализации гонки состоит в том, чтобы перебирать [обещание1, обещание2, ...] и разрешать каждое обещание по порядку.Уведомление: Это проходится по порядку, поэтому гонка не является честной гонкой в строгом смысле, то есть кто-то всегда бежит первым. Здесь promise1 сначала выполняет своего исполнителя, а затем, когда вызывается race, он сначала проходит через Promise.race. Таким образом, обещание, определенное первым, и обещание, помещенное в начало массива, всегда первыми имеют шанс разрешиться.
Итак, здесь, если мы не учитываем этот упорядоченный характер прохождения гонки экземпляров промисов, можно получить неожиданные результаты, как в контрпримерах, которые я перечислил выше.
В первом примере обещание1 теоретически разрешается через 500 миллисекунд, а обещание2 теоретически разрешается через 100 миллисекунд. Я установил таймер 500 мс для Promise.race.Таймер 500 мс дает обещанию1 достаточно времени для разрешения, поэтому, даже если обещание2 разрешается быстрее, результат все равно будет обещанием1.
Насколько я понимаю, во втором примере, когда Promise.race вызывается, в соответствии с исходным кодом race выше, мы можем знать, что race передаст значение разрешения then для разрешения первого завершенного значения Promise для разрешения . Метод then является асинхронным, а это означает, что после вызова then, независимо от того, является ли он разрешением или отклонением, он будет выполнен в следующем цикле, так что это дает некоторые краткосрочные обещания, которые могут закончиться. Таким образом, если время setTimeout в промисе достаточно короткое, то при вызове в первый раз, если предыдущее промис разрешается первым, даже если время setTimeout промиса за массивом короче, он будет разрешать только первое разрешенное значение.
В заключение
Чтобы использовать промисы без неожиданных результатов, нам нужно вызвать Promise.race как можно скорее после определения промисов. На самом деле это предложение не дает полной гарантии того, что Promise.race может справедливо вернуть самое быстрое значение разрешения, например:
let promises = [];
for (let i = 30000; i > -1; i-- ) {
promises.push(new Promise(resolve => {
setTimeout(resolve, i, i);
}))
}
Promise.race(promises).then(function(value) {
console.log(value);
// Both resolve, but promise2 is faster
});
Хотя Promise.race вызывается сразу после определения всех промисов, из-за огромного количества промисов, если оно превышает определенный порог, значение разрешения связано с порядком обхода и скоростью выполнения.
Короче говоря, Promise.race представляет собой последовательный обход, и через метод then функция обратного вызова помещается в очередь событий, таким образом, функция обратного вызова должна пройти повторный последовательный вызов. очередь еще не была выполнена.Если , то Promise.race вернет самое быстрое значение разрешения, но как только некоторые асинхронные операции, которые выполняются быстрее, получат возвращаемый результат до того, как будут пройдены все функции обратного вызова, это может привести к тому, что скорость асинхронного возврата будет fast , но получил неожиданный результат возврата из-за помещения в очередь после более медленной асинхронной операции в очереди событий.