Когда я недавно разбирал методы асинхронного программирования в js, я просмотрел промисы и нашел некоторые важные недостающие знания, такие как значение различных параметров, передаваемых promise.resolve()? Например, порядок событий, когда обещание зависит от другого обещания? Например, когда catch поймает ошибку, будет ли он продолжать выполнять метод then? Следующие разделы ответят на эти вопросы один за другим и вновь подчеркнут некоторые важные моменты.
1. Синтаксис обещания
Основная идея программирования Promise заключается в том,Если данные обещают, то делайте что-нибудь.
Ниже приведен экземпляр обещания.
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Конструктор Promise принимает функцию в качестве параметра, двумя параметрами которого являются разрешение и отклонение.
Роль разрешающей функции, изменить состояние объекта Promise с «незавершенного» на «успешное» (то есть с ожидающего на решенное), вызвать при успешном выполнении асинхронной операции и передать результат асинхронной операции в качестве параметра;
Функция отказа, изменить состояние объекта Promise с «незавершенного» на «сбой» (то есть с ожидающего на отклоненное), вызвать при сбое асинхронной операции и передать сообщение об ошибке асинхронной операции в качестве параметра.
После создания экземпляра Promise метод then можно использовать для указания функции обратного вызова разрешенного состояния и отклоненного состояния соответственно.
promise.then(function(value) {
// success
}, function(error) {
// failure
});
Метод then может принимать в качестве параметров две функции обратного вызова.
Первая функция обратного вызова вызывается, когда состояние объекта Promise становится разрешенным, а вторая функция обратного вызова вызывается, когда состояние объекта Promise становится отклоненным. Среди них вторая функция не является обязательной и не обязательно. Обе эти функции принимают в качестве параметра значение, переданное из объекта Promise.
Ниже приведен пример использования then.Метод then возвращает новый экземпляр Promise.Следовательно, можно использовать метод цепочки, то есть после метода then вызывается другой метод then.
getJSON("/posts.json").then(function(json) {
return json.post;
}).then(function(post) {
// ...
});
В приведенном выше коде используется метод then, по очереди определяющий две функции обратного вызова. После завершения первой функции обратного вызова возвращаемый результат будет передан в качестве параметра второй функции обратного вызова.
Обещание. Прототип. Method.CatchИспользуется для указания функции обратного вызова при возникновении ошибки.
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
В приведенном выше коде метод getJSON возвращает объект Promise; если асинхронная операция выдает ошибку, статус будет отклонен, и для обработки ошибки будет вызвана функция обратного вызова, указанная в методе catch. Кроме того,затем методУказанная функция обратного вызова, если во время работы возникает ошибка, также будет перехвачена методом catch.
Всегда рекомендуется, чтобы за объектом Promise следовал метод catch, чтобы можно было обрабатывать ошибки, возникающие внутри Promise.Метод catch возвращает объект Promise, поэтому метод then можно вызвать позже.
Уведомление:
1. Если функция разрешения и функция отклонения вызываются с параметрами, их параметры будут переданы в функцию обратного вызова. В дополнение к обычному значению параметром функции разрешения также может быть другой экземпляр Promise.
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
})
В приведенном выше коде и p1, и p2 являются экземплярами Promise, но метод разрешения p2 принимает p1 в качестве параметра, то есть результатом асинхронной операции является возврат другой асинхронной операции.
В это время состояние p1 будет передано p2, то есть состояние p1 определяет состояние p2. Если состояние p1 ожидается, то функция обратного вызова p2 будет ждать изменения состояния p1; если состояние p1 разрешено или отклонено, то функция обратного вызова p2 будет выполнена немедленно.
2. Вызов разрешения или отклонения не прерывает выполнение функции параметра промиса.
3. Затем метод определяется на объекте-прототипе Promise.prototype.
4. Если callback-функция для обработки ошибок не указана с помощью метода catch, то ошибка, выброшенная объектом Promise, не будет передана внешнему коду, то есть ответа не будет.
5. Обещание бросает ошибку после оператора разрешения, оно не будет поймано, это означает, что он не брошен. Потому что после того, как состояние обещания меняется, он останется в этом состоянии навсегда и не изменится снова.
2. Promise.resolve()
Иногда необходимо преобразовать существующий объект в объект Promise, и эту роль выполняет метод Promise.resolve.
Promise.resolve эквивалентен следующему.
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
Параметры метода Promise.resolve разбиты на четыре случая.
1) Параметр является экземпляром Promise
Если параметр является экземпляром обещания, обещание. RESOLVE не сделает никаких изменений, возвращающихся к настоящему экземпляру.
2) Параметр является целевой
Объект thenable относится к объекту с методом then, например к следующему объекту.
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
SPOUTY.RESOLE. Метод преобразует объект объекта.
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
В приведенном выше коде после выполнения метода then объекта thenable состояние объекта p1 становится разрешенным, так что функция обратного вызова, указанная последним методом then, выполняется немедленно, и выводится 42.
3) Параметр не является объектом с методом then или вообще не является объектом.
Если аргумент является примитивным значением или объектом без метода then, метод Promise.resolve возвращаетновый объект обещания, статус разрешен.
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
Приведенный выше код генерирует экземпляр p нового объекта Promise. Поскольку строка Hello не является асинхронной операцией (метод оценки состоит в том, что строковый объект не имеет метода then), состояние возвращенного экземпляра Promise разрешается с момента его создания, поэтому функция обратного вызова будет выполнена немедленно. . Одновременно в функцию обратного вызова будут переданы параметры метода Promise.resolve.
4) без каких-либо параметров
Метод Promise.resolve позволяет вызывать без параметров и напрямую возвращает разрешенный объект Promise.
Итак, если вы хотите получить объект Promise, более удобный способ — вызвать метод Promise.resolve напрямую.
const p = Promise.resolve();
p.then(function () {
// ...
});
Переменная p в приведенном выше коде является объектом Promise. должны знать о том,Объекты Promise, которые разрешаются немедленно, находятся в конце текущего «цикла событий», а не в начале следующего «цикла событий».
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
// one
// two
// three
В приведенном выше коде setTimeout(fn, 0) выполняется в начале следующего раунда «цикла событий», Promise.resolve() выполняется в конце этого раунда «цикла событий», console.log(' one') выполняется немедленно, поэтому выведите сначала.
3. Ошибки новичка
1) Как использовать forEach после использования промисов?
// 我想删除所有的docs
db.allDocs({include_docs: true}).then(function (result) {
result.rows.forEach(function (row) {
db.remove(row.doc);
});
}).then(function () {
// 我天真的以为所有的docs都被删除了!
});
Проблема в том, что первая функция на самом деле возвращает значение undefined, а это означает, что второй метод не ожидает выполнения db.remove() для всех документов. На самом деле он ничего не ждет и может выполнить после удаления любого количества документов!
Короче говоря, forEach()/for/while — это не то решение, которое вы ищете. Что вам нужно, это Promise.all():
db.allDocs({include_docs: true}).then(function (result) {
return Promise.all(result.rows.map(function (row) {
return db.remove(row.doc);
}));
}).then(function (arrayOfResults) {
// All docs have really been removed() now!
});
Что означает приведенный выше код? По сути, Promise.all() принимает на вход массив обещаний и возвращает новое обещание. Это новое обещание не вернется, пока все обещания в массиве не будут успешно возвращены. Это асинхронная версия цикла for.
И Promise.all() вернет следующей функции массив результатов выполнения, например, когда вы хотите получить несколько объектов из PouchDB, что очень полезно. Еще более полезным эффектом является то, что Promise.all() вернет ошибку, если какое-либо из промисов в массиве вернет ошибку.
2) забудьте использовать Catch
Просто полагая, что их обещания никогда не подведут, многие разработчики забывают добавить .catch() в свой код. К сожалению, это также означает, что любые выброшенные исключения будут съедены, и вы не сможете наблюдать за ними в консоли. Такие проблемы могут быть очень болезненными для отладки.
3) Используйте вызовы с побочными эффектами вместо возвратов
Что не так с кодом ниже?
somePromise().then(function () {
someOtherPromise();
}).then(function () {
// 我希望someOtherPromise() 状态变成resolved!
// 但是并没有
});
Каждое обещание дает вам функцию then() (или catch(), которая на самом деле является просто синтаксическим сахаром для then(null, ...) ). Когда мы внутри функции then():
somePromise().then(function () {
// I'm inside a then() function!
});
Что мы можем сделать? Есть три вещи:
- вернуть другое обещание
- вернуть значение синхронизации (или не определено)
- генерировать синхронное исключение
Вот и все. Как только вы поймете эту технику, вы поймете обещания.
Итак, давайте посмотрим на них один за другим.
вернуть еще одно обещание
getUserByName('nolan').then(function (user) {
return getUserAccountById(user.id);
}).then(function (userAccount) {
// I got a user account!
});
Уведомление:Яreturn
Второе обещание, этоreturn
Очень важный. если бы я не писалreturn
,getUserAccountById()
будет побочным эффектом, а следующая функция получитundefined
вместоuserAccount
.
Возвращает синхронизированное значение (или неопределенное)
Возврат undefined обычно неверен, но возврат синхронного значения на самом деле является отличным способом обернуть синхронный код в код в стиле промисов. Например, у нас есть кэш информации о пользователях в памяти. Мы можем это сделать:
getUserByName('nolan').then(function (user) {
if (inMemoryCache[user.id]) {
return inMemoryCache[user.id]; // returning a synchronous value!
}
return getUserAccountById(user.id); // returning a promise!
}).then(function (userAccount) {
// I got a user account!
});
Второй функции не нужно заботиться о том, получен ли userAccount синхронным или асинхронным методом, а первая функция может возвращать синхронное или асинхронное значение.
генерировать синхронное исключение
Например, мы хотим создать синхронное исключение, когда пользователь вышел из системы. Было бы очень просто:
getUserByName('nolan').then(function (user) {
if (user.isLoggedOut()) {
throw new Error('user logged out!'); // throwing a synchronous error!
}
if (inMemoryCache[user.id]) {
return inMemoryCache[user.id]; // returning a synchronous value!
}
return getUserAccountById(user.id); // returning a promise!
}).then(function (userAccount) {
// I got a user account!
}).catch(function (err) {
// Boo, I got an error!
});
Если пользователь уже вышел из системы, наш catch() получит синхронное исключение, и он также получит асинхронное исключение в последующих промисах. Опять же, этой функции не нужно заботиться о том, возвращается ли исключение синхронно или асинхронно.
4. URL-адрес ссылки
http://fex.baidu.com/blog/2015/07/we-have-a-problem-with-promises/
http://es6.ruanyifeng.com/#docs/promise
посмотреть оригиналмой блог