о
- Публичный аккаунт WeChat: внешний обруч (Love-FED)
- мой блог:Блог Рауба
- Знать столбец:передний обруч
предисловие
В реальном кодировании мы часто сталкиваемся со сценариями, в которых код Javascript выполняется асинхронно, например вызовы ajax, использование таймера и т. д. В таких сценариях часто появляются такие странные ошибки или плохие фрагменты кода, а затем справиться с ними. Что ж, ваш асинхронный код Javascript становится решающее условие для асинхронного программирования. Давайте начнем с проблемы и шаг за шагом улучшим ваш асинхронный код.
Асинхронная проблема
1. Ад обратного звонка
Во-первых, давайте рассмотрим одну из самых распространенных проблем в асинхронном программировании — ад обратных вызовов. Его появление вызвано неопределенностью времени выполнения асинхронного кода и зависимостями между кодами, такими как:
// 一个动画结束后,执行下一个动画,下一个动画结束后再执行下一个动画
$('#box').animate({width: '100px'}, 1000, function(){
$('#box').animate({height: '100px'}, 1000, function(){
$('#box').animate({left: 100}, 1000);
});
});
Поскольку мы не знаем, когда начинается или заканчивается первая анимация, мы помещаем содержимое выполнения второй анимации в событие окончания первой анимации, а третью анимацию помещаем в конец второй анимации. таких анимаций в настоящее время много, это сформирует ад обратного вызова.
2. Отлавливайте исключения
Помимо callback hell, если нам нужно перехватывать исключения в асинхронном коде, это еще и более хлопотно, и нам может понадобиться вручную настроить метод перехвата:
try {
throw new Error('fail');
} catch (e) {
console.log(e);
}
Такое написание кода, очевидно, не то, что нам нужно, оно не только не способствует сопровождению, но и в определенной степени нарушает хорошие стандарты кодирования Javascript.
решение
Так как мы элегантно пишем наш асинхронный код? Я в основном столбец 5 общих решений:
1. callback
Как следует из названия, обратный вызов является обратным вызовом, но вместо того, чтобы помещать содержимое обратного вызова в асинхронный метод, он помещается во внешнюю функцию обратного вызова.Например, код вопроса 1 можно изменить на этот через обратный вызов:
$('#box').animate({width: '100px'}, 1000, autoHeight);
function autoHeight() {
$('#box').animate({height: '100px'}, 1000, autoLeft);
}
function autoLeft() {
$('#box').animate({left: 100}, 1000);
}
Таким образом, наш, казалось бы, асинхронный код стал синхронным методом записи, который позволяет избежать вложенного метода записи и выглядит намного более плавным. В то же время использование обратного вызова также является самым простым и основным решением для асинхронного программирования.
2. Promise
На основе обратного вызова,Promise
Он также широко используется в настоящее время.Это решение для асинхронного программирования, которое является более разумным и мощным, чем традиционное решение функции обратного вызова. Я считаю, что студенты, которые знают ES6, не будут незнакомы.
Например, теперь у нас есть сценарий, в котором нам нужно асинхронно загрузить изображение и выполнить некоторые операции после того, как изображение будет успешно загружено.Я не хочу использовать функцию обратного вызова или писать логику в событии успеха изображения, поэтому используя Promise, мы можем написать это так:
let p = new Promise((resolve, reject) => {
let img = new Image(); // 创建图片对象
// 图片加载成功事件
img.onload = function() {
resolve(img); // 输出图片对象
};
// 图片加载失败事件
img.onerror = function() {
reject(new Error('load error')); // 输出错误
};
img.src = 'xxx'; // 图片路径
});
// Promise then回调
p
.then(result => {
$('#box').append(result); // 成功后我们把图片放到页面上
})
.catch(error => {
console.log(error); // 打印错误
})
С помощью Promise мы отделяем логику построения и загрузки изображения от логики обработки после успеха или неудачи, меняем вложенность функций обратного вызова на цепные вызовы и используем обратный вызов события отлова Promise для перехвата исключений.Удобство.
Конечно, если вы хотите дождаться завершения нескольких асинхронных запросов и выполнения определенных операций, вы можете использовать метод Promise.all, например:
let p = Promise.all([p1, p2, p3]); // 其中p1、p2、p3都是Promise实例
p.then(result => console.log(result));
Конечно, у Promise есть и соответствующие недостатки.Например, обратный вызов next then может получить только данные, возвращенные предыдущим then, и не может быть получен между слоями.В то же время большое количество обратных вызовов then также сделает код трудно поддерживать.
3. Generator
Как и Promise, функция Generator также является решением для асинхронного программирования, предоставляемым ES6. Она возвращает объект обходчика. Там, где необходимо приостановить асинхронную задачу, мы можем использовать оператор yield, например:
function* getData() {
let result = yield fetch("xxx"); // 调用ajax,yield命令后面只能是 Thunk 函数或 Promise 对象
console.log(result);
}
// 执行
let g = getData();
let result = g.next(); // { value: [object Promise], done: false }
result.value.then(data => {
return data.json();
}).then(data => {
g.next(data);
});
Генератор приостанавливается там, где встречается yield, поэтому нам нужно вручную вызвать следующий метод, чтобы перейти вниз. Атрибут value возвращаемого значения next — это данные, которые нам нужны. Вот объект Promise, возвращаемый методом fetch, поэтому мы нужно использовать тогда обратный вызов Наконец, вызовите g.next(data) для завершения и вывода данных.
Недостаток функции Generator в том, что каждый раз, когда мы выполняем оператор yield, нам нужно вручную переходить к следующему, что не очень удобно.
4. co
Чтобы решить проблему, связанную с тем, что вышеприведенная функция Generator должна вручную выполнять следующий метод, TJ Holowaychuk написал библиотеку функций co, которая позволяет выполнять функцию Generator автоматически.Например, нам нужно это:
let files = function* (){
var f1 = yield readFile('/xxx/xxx'); // 读取file1文件
var f2 = yield readFile('/xxx/xxx'); // 读取file2文件
console.log(f1.toString());
console.log(f2.toString());
};
files.next(); // 执行yield
files.next(); // 执行yield
После использования co:
var co = require('co');
co(files);
Функция co возвращает объект Promise, поэтому с помощью метода then можно добавить функцию обратного вызова.
co(files).then(() => {
console.log('执行完成');
});
Наконец, мы видим, что мы не выполнили следующий метод вручную, а также не распечатали прочитанный файл.
Хотя модуль co помог нам решить проблему, связанную с тем, что функция Generator должна полагаться на исполнителя, нам всем нужно ввести дополнительный модуль для его использования, так что есть ли более удобный способ решить эту проблему? Продолжайте смотреть вниз.
5. async and await
В дополнение к вышеперечисленным 4 способам решения проблемы асинхронного программирования в Javascript, ES7 также предоставляет более удобные асинхронные функции и команды ожидания, понимаете?
По сути, асинхронность — это синтаксический сахар функции-генератора, разница в том, что у нее есть встроенный исполнитель, а значит, у асинхронной функции есть свой исполнитель. Взгляните на следующий пример:
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
}, 1000);
});
async function waitFn() {
let a = await p1; // await命令后面可以是 Promise 对象和原始类型的值,如果使原始类型最终也会返回为Promise对象
let b = await p2;
return a + b
}
// async函数的返回值是 Promise 对象, 可以用then方法指定下一步的操作
waitFn().then(result => {
console.log(result); // 2s后输出3
});
Значение, возвращаемое оператором return внутри асинхронной функции, станет параметром функции обратного вызова метода then. Так что это похоже на функцию-генератор, обернутую в co, просто замените * на async и замените yield на await.
Можно сказать, что асинхронность и ожидание — одна из самых важных функций в ES7, хотя у нее есть и некоторые недостатки, ее относительно удобно использовать для работы с асинхронным кодом.
Эпилог
В этой статье кратко представлены пять распространенных способов обработки асинхронного кода Javascript.Каждый метод имеет свои условия и необходимость для его использования и существования.Заинтересованные студенты могут расширить и изучить их по отдельности.Только путем понимания и освоения каждого метода.Только с использованием преимуществ каждого метода. и используя их, вы можете наслаждаться удовольствием, которое приносит асинхронное программирование.