1. Введение в промисы
Промисы — это решение для асинхронного программирования, и их первоначальная цель состояла в том, чтобы решить проблему ада обратных вызовов.
Например, мне нужно: --(задержка 1 с) --> вывод 1 --(задержка 2 с) --> вывод 2 --(задержка 3 с) --> вывод 3, обычно пишется так:
setTimeout(()=> {
console.log('1');
setTimeout(()=> {
console.log('2');
setTimeout(()=> {
console.log('3');
}, 3000)
}, 2000)
}, 1000)
Такие множественные вложенные обратные вызовы называются адом обратных вызовов, и такой код плохо читается и сложен для понимания.
Если вы используете обещания, стиль рисования изменится
function delay(time, num) {
return new Promise((res, rej)=> {
setTimeout(()=> {
console.log(num);
res();
}, time*1000)
});
}
delay(1, 1).then(()=> {
return delay(2, 2);
}).then(()=> {
delay(3, 3);
})
Используется цепной вызов промисов, а структура кода понятнее.
Разве это не здорово? Тогда не торопись и бери~
2. Использование обещаний
Метод вызова следующий:
new Promise((resolve, reject)=> {
if('some option') {
resolve('some value');
} else {
reject('some error');
}
}).then(
val=> {
// ...
},
error=> {
// ...
}
)
Конструктор Promise получает функциональный параметр fn, fn имеет два параметра, а именно: resolve, reject, Promise также имеет метод Promise.prototype.then, который получает два параметра, а именно успешный обратный вызов функции succ и отказ функции обратного вызова error.
Вызов разрешения в fn вызовет обратный вызов succ, а вызов reject вызовет обратный вызов ошибки.
2.1 Передача параметров
- Параметры, переданные при вызове разрешения/отклонения внутри fn, будут переданы в соответствующую функцию обратного вызова в качестве соответствующих параметров.
new Promise((res, rej)=> {
res('happy')
}).then(val=> {
console.log(val); // happy
});
new Promise((res, rej)=> {
rej('error!');
}).then(val=> {}, err=> {
console.log(err); // error!
});
- При цепочке вызовов, если на предыдущий уровень не передается значение, значение по умолчанию не определено.
new Promise((res, rej)=> {
res('a');
}).then(val=> {
return 'b'
}).then(val=> {
console.log(val); // 'b'
}).then((val)=> {
console.log(val); // 'undefined'
});
- Если значение then, переданное на предыдущем уровне, не является функцией, этот уровень игнорируется.
new Promise((res, rej)=> {
res('a');
}).then(val=> {
return 'b';
}).then(val=> {
console.log(val); // 'b'
return 'c';
}).then({ // 并非函数
name: 'lan'
}).then((val)=> {
console.log(val); // 'c'
});
2.2 Пример передачи параметров
let doSomething = function() {
return new Promise((resolve, reject) => {
resolve('返回值');
});
};
let doSomethingElse = function() {
return '新的值';
}
doSomething().then(function () {
return doSomethingElse();
}).then(resp => {
console.warn(resp);
console.warn('1 =========<');
});
doSomething().then(function () {
doSomethingElse();
}).then(resp => {
console.warn(resp);
console.warn('2 =========<');
});
doSomething().then(doSomethingElse()).then(resp => {
console.warn(resp);
console.warn('3 =========<');
});
doSomething().then(doSomethingElse).then(resp => {
console.warn(resp);
console.warn('4 =========<');
});
В сочетании с приведенным выше объяснением, каков будет результат? (Ответы и анализ)
3. Promise.prototype.then
Когда состояние в Обещании (pending ---> resolved or rejected) будет выполняться только при изменении метода then.
- Позвоните тогда вернули еще пример обещания (так что это можно привязать звонки ...)
new Promise((res, rej)=> {
res('a');
}).then(val=> {
return 'b';
});
// 等同于
new Promise((res, rej)=> {
res('a');
}).then(val=> {
return new Promise((res, rej)=> {
res('b');
});
});
- Обратные вызовы в then всегда выполняются асинхронно
new Promise((res, rej)=> {
console.log('a');
res('');
}).then(()=> {
console.log('b');
});
console.log('c');
// a c b
- Если вы не вызываете разрешение или отклонение в функции параметра обещания, тогда метод then никогда не будет запущен.
new Promise((res, rej)=> {
console.log('a');
}).then(()=> {
console.log('b');
});
console.log('c');
// a c
4. Статический метод Promise
Promise также имеет четыре статических метода:resolve,reject,all,race, давайте представим их один за другим.
4.1 Promise.resolve()
В дополнение к новому методу Promise() у нас также есть два метода для создания объектов Promise: Promise.resolve() эквивалентен созданию объекта с немедленным разрешением. Следующие два фрагмента кода работают одинаково:
Promise.resolve('a');
new Promise((res, rej)=> {
res('a');
});
Конечно, Promise.resolve() будет выполнять разные операции в зависимости от переданных параметров.
- Параметр является экземпляром Promise
Если аргумент является экземпляром Promise, то Promise.resolve вернет экземпляр без изменений и без изменений.
- Параметр является доступным объектом
Объект thenable относится к объекту с методом then, например к следующему объекту.
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
Метод Promise.resolve преобразует этот объект в объект Promise, а затем немедленно выполнит метод then объекта, который можно использовать.
- аргумент не является объектом с методом then или вообще не является объектом
Если аргумент является примитивным значением или объектом без метода then, метод Promise.resolve возвращает новый объект Promise с состоянием разрешения.
- без параметров
Метод Promise.resolve позволяет вызывать без параметров и напрямую возвращает разрешенный объект Promise.
Кое-что стоит отметитьОн заключается в том, что статический метод вызывается перед окончанием опроса этого события, а не в начале опроса следующего события. Про опрос событий можно посмотреть здесь -->Подробное объяснение механизма работы JavaScript: снова поговорим о цикле событий
4.2 Promise.reject()
Аналогичен Promise.resolve(), за исключением того, что один из них является обратным вызовом, который вызывает успех, а другой — обратным вызовом, который вызывает сбой.
4.3 Promise.all()
Метод all Promise предоставляет возможность выполнять асинхронные операции параллельно и выполнять обратные вызовы после выполнения всех асинхронных операций.
function asyncFun1() {
return new Promise((res, rej)=> {
setTimeout(()=> {
res('a');
}, 1000);
});
}
function asyncFun2() {
return new Promise((res, rej)=> {
setTimeout(()=> {
res('b');
}, 1000);
});
}
function asyncFun3() {
return new Promise((res, rej)=> {
setTimeout(()=> {
res('c');
}, 1000);
});
}
Promise.all([asyncFun1(), asyncFun2(), asyncFun3()]).then((val)=> {
console.log(val);
});
Promise.all([asyncFun1(), asyncFun2(), asyncFun3()]).then((val)=> {
console.log(val); // ['a', 'b', 'c']
});
Используйте Promise.all для выполнения, all получает параметр массива, а значения в нем в итоге вернутся в объект Promise. Таким образом, три асинхронные операции выполняются параллельно, ожидая, пока они не будут выполнены.после того, как все казненывойти тогда. При этом вы можете выполнять несколько асинхронных операций параллельно и обрабатывать все возвращаемые данные в одном обратном вызове.
Применимые сценарии: при открытии веб-страницы предварительно загрузите различные ресурсы, которые необходимо использовать, такие как изображения, флэш-память и различные статические файлы. После того, как все загружено, приступаем к инициализации страницы.
4.4 Promise.race()
Race() противоположна всем, all() должна выполняться после того, как все промисы в массиве будут выполнены, а race() должна выполняться then() после выполнения промиса, используя три вышеупомянутые функции возвращаемого значения промиса как Примеры
Promise.race([asyncFun1(), asyncFun2(), asyncFun3()]).then((val)=> {
console.log(val); // a
});
5. Классический пример цепного вызова
Прочитав так много знаний о Promise, давайте зададим вопрос, чтобы закрепить их.
写一个类Man实现以下链式调用
调用方式:
new Man('lan').sleep(3).eat('apple').sleep(5).eat('banana');
打印:
'hello, lan' -(等待3s)--> 'lan eat apple' -(等待5s)--> 'lan eat banana'
Идеи:
- Верните это в метод прототипа, чтобы достичь цели цепочки вызовов.
- Эффект ожидания выполнения 3 с может быть достигнут с помощью Promise, а затем
Конкретная реализация выглядит следующим образом:
class Man {
constructor(name) {
this.name = name;
this.sayName();
this.rope = Promise.resolve(); // 定义全局Promise作链式调用
}
sayName() {
console.log(`hello, ${this.name}`);
}
sleep(time) {
this.rope = this.rope.then(()=> {
return new Promise((res, rej)=> {
setTimeout(()=> {
res();
}, time*1000);
});
});
return this;
}
eat(food) {
this.rope = this.rope.then(()=> {
console.log(`${this.name} eat ${food}`);
});
return this;
}
}
new Man('lan').sleep(3).eat('apple').sleep(5).eat('banana');
OK! Я не знаю, понимаешь ли ты? Если вы можете полностью понять код, ваше обещание можно очистить, кстати, у вас есть небольшое мышление? В чем разница, чем выше? :
class Man1345 {
constructor(name) {
this.name = name;
this.sayName();
}
sayName() {
console.log(`hello, ${this.name}`);
}
sleep(time) {
this.rope = new Promise((res, rej)=> {
setTimeout(()=> {
res();
}, time*1000);
});
return this;
}
eat(food) {
this.rope = this.rope.then(()=> {
console.log(`${this.name} eat ${food}`);
});
return this;
}
}
new Man('lan').sleep(3).eat('apple').sleep(5).eat('banana');
Проще говоря, результат выполнения второго куска кода
'hello, lan' -(等待3s)--> 'lan eat apple' ---> 'lan eat banana'
Почему возникает эта разница? Потому что второй фрагмент кода будет создавать новый объект Promise каждый раз, когда вызывается sleep, а два объекта Promise будут новыми после двойного вызова sleep. Эти два объекта выполняются асинхронно и параллельно, что приведет к одновременному отображению двух съедений.
Похожий на
var time1 = setTimeout(()=> {
console.log('a');
}, 1000)
var time2 = setTimeout(()=> {
console.log('b');
}, 1000)
// 同时输出 a b
Абстрактное объяснение:
// 第一段正确的代码的执行为
var p1 = new Promise().then('停顿3s').then('打印食物').then('停顿5s').then('打印食物');
// 第二段代码的执行行为,p1、p2异步并行执行
var p1 = new Promise().then('停顿3s').then('打印食物');
var p2 = new Promise().then('停顿5s').then('打印食物');
Суммировать
Обещания часто используются в следующих местах:
- Выбраться из ада обратного вызова
- Несколько асинхронных задач
Promise — хороший помощник для нас, но есть и другой способ сделать это — async&await, вы можете узнать о нем подробнее.
использованная литература