предисловие
В сезон смены работы из года в год я готов начать с содержания интервью и взглянуть на точки знаний, связанные с интерфейсом, стремясь изучить ряд точек знаний и углубленное исследование в рамках моей компетенции. способность. Сосредоточьтесь на практике. Для основного интерфейса и студентов, готовящихся к собеседованиям, попробуйте прикрепить актуальные примеры кода и связанные с ними тестовые вопросы. Название серии будет [лысое собеседование по интерфейсу] - потому что все в кругу согласны, технология пропорциональна объему волос. 😄 Желаем всем доброго утралысыйпреодолеть узкое место
Слишком много статей о вопросах собеседования или определенной точке знаний.Вот хочу просто записать свое личное резюме в виде репозитория кода и вывести статью.Ведь теория не то же самое, что практика, и если вы знаете что это такое, вы должны знать, почему. Только тогда мы сможем по-настоящему понять~
Похожие серии статей:
- Лысые и сломанное интерфейсное интервью - обещание && async / ждать
- Лысый сломанный интерфейс интервью - прототип и наследство - еще не написано
- Лысый сломанный фронт-энд-интервью - закрытие - еще не написано
- Интервью с лысым сломанным интерфейсом - краткое изложение междоменной практики
- ...
Promise
Предпосылка обещания
У всего есть причина и следствие, и появление новых вещей означает, что старые вещи не могут удовлетворить наши потребности. В этом контексте появляется новая вещь Promise, а старая вещь, которую он заменяет, — это callback (функция обратного вызова), которая часто использовалась до ES6.
Хотя промисы ES6 не новы, но для контекста они появились, чтобы решить проблему ада асинхронных обратных вызовов.
ад обратного звонка
Что такое callback hell, рассмотрим простейший пример:
setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
setTimeout(() => {
console.log(444);
// 你还可以放置更多
...
}, 4000);
}, 3000);
}, 2000)
}, 1000);
Вообще говоря, callback hell происходит при асинхронных операциях, и следующая операция зависит от результата предыдущей.У нас невыносимая головная боль и написанный выше код.
Конечно, вышеприведенное немного халтурно для черноты, на самом деле часто используемые сценарии должны заключаться в том, что AJAX-запросы и различные операции с базой данных будут генерировать callback hell. Следующий код представляет собой операцию со стандартной таблицей запросов к базе данных несколько раз (здесь я проверил ее только дважды, но она также была вложенной).
/**
* 回调地狱示例
*/
const db = Object.create(null); // 假设这就是连接数据库的对象
/**
* 第一步,从 A 表查出 id 为 1 的用户
* 第二步,从 B 表查出文章作者是 id = 1 用户 username 的所有文章
**/
db.query('SELECT * FROM A WHERE id = 1', function(err, results) {
if (err) throw err;
// 完成第一步,开始第二步
db.query(`SELECT * FROM B WHERE author = ${results[0].username}`, function(err, results) {
if (err) throw err;
// 完成第二步,开始干坏事
console.log(results);
});
});
Приведенный выше код, если вы продолжите его проверять, должен не слишком отличаться от приведенного выше кода, и запрос к базе данных действительно может иметь описанную выше ситуацию.
Promise решает проблему асинхронности и избегает ада обратных вызовов
Если есть проблема, ее нужно решить.Появляется Promise.Давайте сначала посмотрим, как Promise решает ад колбэков.
// promise 解决
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
}).then(data => console.log(data));
}
function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(222), 2000);
}).then(data => console.log(data));;
}
function f3() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(333), 3000);
}).then(data => console.log(data));;
}
function f4() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(444), 4000);
}).then(data => console.log(data));;
}
f1().then(f2).then(f3).then(f4);
Ну этот вид, он действительно решается, нет вложенной функции, а потом вызов тоже стал цепным вызовом. Конечно, этот пример также немного особенный, и, в свою очередь, посмотрите на пример данных запроса к базе данных:
/**
* 使用 Promise
* 因为 Promise 是 ES6,所以下面所有代码都使用 ES6 语法
**/
new Promise((resolve, reject) => {
db.query('SELECT * FROM A WHERE id = 1', (err, results) => {
if (err) reject(err);
resolve(results);
});
}).then(data => {
// 拿到第一步数据,开始第二步
db.query(`SELECT * FROM B WHERE author = ${results[0].username}`, (err, results) => {
if (err) reject(err);
// 完成第二步,开始干坏事
console.log(results);
});
}).catch(err => {
throw err;
});
Наоборот, выглядит лучше.
Основы обещаний
Объект Promise используется для представления возможного завершения (или сбоя) асинхронной операции и ее результирующего значения. Объект Promise является прокси-объектом (проксирует значение), и проксируемое значение может быть неизвестно при создании объекта Promise. Это позволяет вам связывать отдельные обработчики для успеха и неудачи асинхронных операций. Это позволяет асинхронным методам возвращать значения, подобные синхронным методам, но вместо немедленного возврата конечного результата выполнения — объект-обещание, представляющий будущий результат.
Похоже, что это решило проблему ада обратных вызовов, вызванную часто вложенными функциями обратного вызова в коде JS до ES6.Promise — это функция ES6.
Состояние обещания
Значение объекта Promise неизвестно, и его состояние можно изменить, но независимо от того, как оно изменяется, его состояние всегда находится между следующими тремя:
- pending: исходное состояние, ни успеха, ни отказа.
- выполнено: означает, что операция завершена успешно.
- отклонено: означает, что операция не удалась.
Состояние Обещания изменится, и когда оно будет выполнено, оно изменится сpending -> fulfilled, который выходит из строя изpending -> rejected, но этот процесс необратим, т. е. не может перейти из двух других состояний вpending.fulfilled/rejectedЭти два состояния также называют оседлым состоянием.
Обещания использования
Все в JS является объектом, поэтому мы также можем использовать промисы.newпублично заявить. Мы создаем новый объект Promise со следующим синтаксисом:
new Promise( function(resolve, reject) {...} /* executor */ );
Конструктор промиса принимает один параметр - он принимает два параметра(resolve, reject)Два параметра представляют результат этой асинхронной операции, то есть состояние промиса.resolveа такжеrejectКогда функция вызывается, она меняет состояние этого промиса наfulfilledилиrejected, после завершения асинхронной операции конечное состояние промиса может быть только одним из двух.Если асинхронная операция прошла успешно, состояние будет заменено наresolveФункция изменена наfullfilled; И наоборот, когда во время асинхронного процесса выдается ошибка, состояние будетrejectфункция изменена наrejected.
Promise API
Цепочка прототипов Promise и сам объект имеют несколько методов, которые мы можем использовать, среди которых наиболее часто используемые и условно говоря следующие:
then -- Promise.prototype.then(onFulfilled, onRejected)
Добавьте обратные вызовы выполнения и отклонения к текущему обещанию, возвращая новое обещание, которое будет разрешено с возвращаемым значением обратного вызова.
Это всегда кажется неясным и трудным для понимания, поэтому мы должны смотреть на реальный код:
new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
}).then(data => {
console.log(data);
});
new Promise((resolve, reject) => {
setTimeout(() => reject(111), 1000);
}).then(data => {
console.log(data);
});
можно увидеть,.thenТо, что мы получаем внутри, — это данные после разрешения нашего промиса. И он также вернет Promise для вызова, например:
new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
}).then(data => {
console.log(data); // 打印 111
return data + 111; // 相当于 resolve(data + 111)
}).then(data => {
console.log(data); // 打印 222
});
then()Использование относительно простое, и каждый должен использовать его часто.На самом деле, вы знаете его здесь..then()Это цепочка называется, потому что это также обещание, это возможно.
catch -- Promise.prototype.catch(onRejected)
Добавляет обратный вызов отклонения к текущему промису, возвращая новый промис. Когда эта функция обратного вызова вызывается, новое обещание будет разрешено с его возвращаемым значением, в противном случае, если текущее обещание переходит в состояние выполнено, выполненный результат текущего обещания будет использоваться как выполненный результат нового обещания.
new Promise((resolve, reject) => {
setTimeout(() => reject(111), 1000);
}).then(data => {
console.log('then data:', data);
}).catch(e => {
console.log('catch e: ', e);
});
Как показано на рисунке выше: Вообще говоря, написание catch означает, что произошло исключение, и в целом оно закончилось, но из документации, оно также возвращает промис, я сказал, что не использовал его таким образом, но все же поэкспериментируйте Бар:
new Promise((resolve, reject) => {
setTimeout(() => reject(111), 1000);
}).then(data => {
console.log('then data:', data);
}).catch(e => {
console.log('catch e: ', e);
return e;
}).then(data => {
console.log('catch data: ', data);
});
Хорошо, я поднял свою позу, но я все же говорю, что лично я считаю, что этого достаточно, чтобы поймать ошибку, нет необходимости делать следующий шаг, если только вы не хотите использовать ошибку для других вещей~
наконец - Promise.prototype.finally()
упомянутый вышеcatch()Обычно используется для перехвата ошибок, поэтому большая часть кода должна заканчиваться на этом шаге, но на самом деле Promise предоставляет стандартный метод завершения.finally(), пока состояние Promise становится стабильным, независимо от того, отклонено оно или выполнено, оно будет окончательно зафиксировано.
new Promise((resolve, reject) => {
setTimeout(() => reject(111), 1000);
}).then(data => {
console.log('then data:', data);
}).catch(e => {
console.log('catch e: ', e);
return e;
}).then(data => {
console.log('catch data: ', data);
return data;
}).finally(() => {
console.log('promise finally');
return 222;
}).then(data => {
console.log('finally data: ', data);
});
С графика вы можете увидеть, наконец, это вернет обещание, но я советую вам хорошо, чтобы наконец-то действительно может в конечном итоге! ! ! Просто чтобы продемонстрировать его возвращение здесь.
Я думал об этом, и причина, по которой я не использую его, может быть в том, что я слишком низок.На самом деле, у него есть еще очевидные пробные сценарии. Например, официальная демоверсия:
let isLoading = true;
fetch(myRequest).then(function(response) {
var contentType = response.headers.get("content-type");
if(contentType && contentType.includes("application/json")) {
return response.json();
}
throw new TypeError("Oops, we haven't got JSON!");
})
.then(function(json) { /* process your JSON further */ })
.catch(function(error) { console.error(error); /* this line can also throw, e.g. when console = {} */ })
.finally(function() { isLoading = false; });
Этот сценарий должен быть очень распространенным в реальном процессе разработки.Если finally не используется, мы установим его один раз в then и перехватим соответственно.isLoading = false;, а использование finally нужно назначить только один раз, что не только позволяет избежать дублирования кода, но и оптимизирует логику ~ это правильный способ использования ~
Promise.finally(fn) необходимо обратить внимание на следующие два момента:
- Параметр fn — это функция без аргументов, независимо от того, выполнено обещание в конечном итоге или отклонено.
- finally не меняет состояние промиса.
все - Promise.all(итерируемый)
Этот метод возвращает новый объект обещания, который сработает, когда все объекты обещания в объекте параметра iterable будут успешными. После того, как новый объект обещания инициирует состояние успеха, он будет использовать массив, содержащий все возвращаемые значения обещания в итерируемом объекте, в качестве возвращаемого значения обратного вызова успеха в том же порядке, что и итерируемый; если новый объект обещания вызывает сбой состояние, это будет сообщение об ошибке первого неудачного промиса в итерируемом объекте, которое будет использоваться как его сообщение об ошибке сбоя. Метод Promise.all часто используется для обработки коллекции состояний нескольких объектов-обещаний.
Это API, который я часто использую. Хотя приведенный выше контент немного длинный, на самом деле его очень просто обобщить. Вероятно, он состоит из следующих трех аспектов:
- Во-первых: получить массив объектов Promise в качестве параметра
// promise 解决
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
}).then(data => console.log(data));
}
function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(222), 2000);
}).then(data => console.log(data));;
}
function f3() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(333), 3000);
}).then(data => console.log(data));;
}
Promise.all([f1, f2, f3]);
- Второе: все обратные вызовы параметров успешны, а массив возвращаемых значений находится в том же порядке, что и параметры.
// promise 解决
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
});
}
function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(222), 2000);
});;
}
function f3() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(333), 3000);
});;
}
Promise.all([f1(), f2(), f3()]).then(results => {
console.log(results);
});
Видно, что возвращаемое значение представляет собой массив, и каждый элемент соответствует соответствующему значению разрешения в массиве параметров.
- Третье: если один из параметров в массиве дает сбой, активируется статус сбоя, и первое сообщение об ошибке Promise с ошибкой используется в качестве сообщения об ошибке Promise.all.
// promise 解决
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
});
}
function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => reject(222), 2000);
});;
}
function f3() {
return new Promise((resolve, reject) => {
setTimeout(() => reject(333), 3000);
});;
}
Promise.all([f1(), f2(), f3()]).then(results => {
console.log(results);
}).catch(e => {
console.log(e);
});
Видно, что когда я устанавливаю второе и третье значение для отклонения соответственно, Promise.all входит в стадию перехвата, то есть захвата исключений, и захватывается содержимое второго отклонения, которое является первым вхождением. .
Поэтому в общем случае Promise.all используется для обработки нескольких одновременных запросов, а также для удобства построения данных страницы.Он запрашивает данные, используемые страницей, на разных интерфейсах вместе.Однако, если один из интерфейсов выходит из строя, больше Запрос будет неудачным, и страница может не выйти, это зависит от степени связанности текущей страницы~
race
Когда любое из дочерних обещаний в итерируемом параметре успешно или неудачно, родительское обещание немедленно вызовет соответствующий дескриптор, связанный с родительским обещанием, с возвращаемым значением успеха или сведениями об ошибке дочернего обещания в качестве параметров и вернет объект обещания.
Этот API разумен и используется нечасто, но в некоторых сценариях он все же очень мощный. Как сказать, буквальное значение — конкуренция. Представьте себе сценарий, в котором пользователи входят в систему и отменяют ее. Процесс входа в систему — это процесс запроса, который займет время. Предположим, я нажимаю на вход здесь и нажимаю «Отмена» во время процесса запроса данных. Если вход в систему не имеет Когда ответ возвращается, следует отменить это поведение, чтобы выиграть соревнование, а не входить в систему.
Конечно, на самом деле я не использовал сценарий отмены входа в систему, я использовал Promise.race только в одном месте - тайм-аут выборки. Как мы все знаем, если интерфейс использует запрос на выборку, нет возможности установить время ожидания. , потому что внутри fetch нет тайм-аута. С этим параметром, если мы хотим, чтобы внешний интерфейс установил тайм-аут, например, если нет данных ответа более 5 с, запрос считается истекшим. мы можем использовать Promise.race, чтобы помочь нам достичь этого. Поскольку выборка по сути является промисом, нам нужно выполнять выборку гонки только с промисом, который отклоняется/разрешается после 5-секундной задержки в Promise.race. Конкретный код выглядит следующим образом:
// fetch timeout实现
timeoutPromise = () => new Promise((resolve) => {
setTimeout(() => {
resolve(
new Response(
'Timeout',
{
status: 408,
statusText: "Fetch timeout",
ok: false
}
)
);
}, timeout = 5000);
});
Promise.race([timeoutPromise(), fetch(url, opts)])
.then(res => res.json())
.then(data => {
return data;
});
Поскольку я предпочитаю использовать выборку, я использую эту сцену, и она доступна для личного тестирования. Вы можете изменить конкретные детали в соответствии с вашим собственным проектом. Я не буду вводить здесь слишком много. Если вам интересно, вы можете оставить сообщение для обмена.
Написание обещания от руки
В этот момент кто-то должен спросить, вам нужно написать еще один промис вручную?конечно, нет!Как я уже сказал, упор делается на практику.С практической точки зрения, я не думаю, что кто-то будет использовать в проекте свои собственноручные промисы, но они будут напрямуюnew Promise(),Поэтому я потрачу свое время и время всех на то, чтобы написать промис, который никто не будет использовать, да и бессмысленно.Если хотите разобраться во внутренней реализации, то рекомендуется сразу перейти к исходникам~
Async/Await
Мы должны сделать это снова.Появление новых вещей означает, что старые вещи не могут удовлетворить наши потребности.ES6 только что выпустила Promise для решения проблемы асинхронности, а ES7 выпустила еще один Async/Await (на самом деле, официальное название - асинхронная функция). Похоже, Promise не оправдал ожиданий большого парня, поэтому чиновник придумал более элегантное асинхронное решение.
Почему говорят, что решить проблему, вызванную обещанием, вы можете взглянуть на следующий параграф на официальном сайте MDN:
Целью async/await является упрощение синхронного поведения при использовании нескольких промисов и выполнение определенных операций над набором промисов. Точно так же, как промисы похожи на структурированные обратные вызовы, async/await больше похож на комбинацию генераторов и промисов.
Обещания — не идеальное решение
В примере с асинхронным вложенным setTimeout, упомянутым выше, на самом деле большинство людей используют промисы не для написания кода, подобного приведенному выше, а для следующего:
/* Async/Await */
new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
}).then(data => {
console.log(data);
new Promise((resolve, reject) => {
setTimeout(() => resolve(222), 2000)
}).then(data => {
console.log(data);
new Promise((resolve, reject) => {
setTimeout(() => resolve(333), 3000)
}).then(data => {
console.log(data);
new Promise((resolve, reject) => {
setTimeout(() => resolve(444), 4000)
}).then(data => {
console.log(data);
})
})
})
});
Ну, если честно, на самом деле, если Promise.then() использовать слишком много, это все равно будет callback hell, и вложенность никуда не делась, поэтому Promise нельзя назвать идеальным асинхронным решением, поэтому ES7 предлагает асинхронность. функция, она используется для более элегантного решения асинхронности. Давайте посмотрим на его очарование на этот раз:
// 定时器嵌套
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
}).then(data => console.log(data));
}
function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(222), 2000);
}).then(data => console.log(data));;
}
function f3() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(333), 3000);
}).then(data => console.log(data));;
}
function f4() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(444), 4000);
}).then(data => console.log(data));;
}
async function timeoutFn() {
await f1(); // 开始执行第一个异步函数
await f2(); // 第一个执行完,开始执行第二个异步函数
await f3(); // 第二个执行完,开始执行第三个异步函数
await f4(); // 第三个执行完,开始执行第四个异步函数
}
timeoutFn();
// 数据库查询
async function queryData() {
try {
// 第一步,获取数据
const step1Data = await db.query('SELECT * FROM A WHERE id = 1');
// 第二步,获取数据
const step2Data = await db.query(`SELECT * FROM B WHERE author = ${step1Data[0].username}`);
console.log(step2Data);
} catch(e) {
throw e;
}
}
Посмотрите на приведенный выше код, какой красивый, полностью синхронный процесс — его без преувеличения можно назвать самым совершенным асинхронным решением.
Основа асинхронной функции
Об Async Function, нет слишком много API, потому что он больше похож на старший синтаксический сахар, и официальный документ дает больше примеров использования. Здесь, на самом деле, нам нужно только знать и подчеркивать один -Ключевое слово await используется для приостановки и ожидания завершения выполнения асинхронной функции.Если это промис, это означает ожидание его установленного состояния, а ожидание может появляться только внутри асинхронной функции и не может использоваться отдельно..
Пример
Чиновник привел более интересный пример:
// 一个1秒的异步函数
var resolveAfter1Second = function() {
console.log("starting fast promise");
return new Promise(resolve => {
setTimeout(function() {
resolve("fast");
console.log("fast promise is done");
}, 1000);
});
};
// 一个2秒的异步函数
var resolveAfter2Seconds = function() {
console.log("starting slow promise");
return new Promise(resolve => {
setTimeout(function() {
resolve("slow");
console.log("slow promise is done");
}, 2000);
});
};
// 下面这种写法是一起执行异步函数,只不过因为await等待导致输出有先后
var concurrentStart = async function() {
console.log('==CONCURRENT START with await==');
const slow = resolveAfter2Seconds(); // starts timer immediately
const fast = resolveAfter1Second(); // starts timer immediately
// 1. Execution gets here almost instantly
console.log(await slow); // 2. this runs 2 seconds after 1.
console.log(await fast); // 3. this runs 2 seconds after 1., immediately after 2., since fast is already resolved
}
// 下面这种是标准的等待写法
var sequentialStart = async function() {
console.log('==SEQUENTIAL START==');
// 1. Execution gets here almost instantly
const slow = await resolveAfter2Seconds();
console.log(slow); // 2. this runs 2 seconds after 1.
const fast = await resolveAfter1Second();
console.log(fast); // 3. this runs 3 seconds after 1.
}
В частности, вы можете на самом деле испытать это на себе.Второй нечего сказать.Это как это в воображении, потому что ожидание будет останавливаться и ждать выполнения функции перед выполнением вниз, поэтому время ожидания не будет перекрываться, и ждать в течение 2 секунд для выполнения сначала медленно, а затем подождите 1 секунду для быстрого выполнения.
И первый
const slow = resolveAfter2Seconds();
const fast = resolveAfter1Second();
console.log(await slow);
console.log(await fast);
Вышеупомянутые две асинхронные функции выполняются немедленно, поскольку ключевое слово await отсутствует, поэтому они выводятся первыми.promise start, после этого две функции имеют разные задержки.Хотя сначала выполняется медленная, она составляет 2 секунды, а быстрая выполняется через 1 секунду, а вывод идет первымfast doneвывод сноваslow done. Наконец, в игру вступает ключевое слово await.Хотя быстрое выполняется первым, вам все равно нужно дождаться завершения ожидания медленного, прежде чем ждать быстрого.
Суммировать
Я не буду здесь приводить актуальные вопросы интервью, если вы понимаете предысторию и базовый контент, и умеете пользоваться API, то вопросы интервью будут те же, и говорить не о чем. Когда я писал это, я вдруг подумал о вопросе, так что давайте поговорим об этом. И setTimeout, и Promise — асинхронные операции, так что же быстрее?
function whoFast() {
setTimeout(() => console.log('settimeout'), 0);
new Promise(() => {
console.log('promise');
})
}
Практика - единственный критерий проверки на истину.Промисы выполняются быстрее вне зависимости от порядка.Что касается принципа,перейдем к механизму событийного цикла js.Если интересно,можете и позже написать~
Пополнить
Первые несколько статей я думал, что они были хорошо написаны, но мало кто их читал, а в этой не было ощущения, что я что-то написал, но многие люди комментировали. Я действительно этого не ожидал, поэтому есть некоторые детали, которые я не не считай, 😄. Чтобы добавить сюда:
Дополнение 1: Promise.allSettled (итерируемый)
упомянутый выше,Promise.all([])Если есть исключение, то первая ошибка будет возвращена сразу, поэтому даже если некоторые из них будут успешными, они не будут возвращены.Иногда с этим будут проблемы.На одной странице два интерфейса,используяPromise.all()Чтобы получить его, если один преуспевает, а другой терпит неудачу, вы должны по крайней мере показать успешный, гм, поэтому он используется в настоящее время.Promise.allSettled(), он также возвращает соответствующий массив, который является установленным состоянием соответствующего промиса, который может быть успешным или неудачным~
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// expected output:
// "fulfilled"
// "rejected"
Дополнение 2: Что происходит, когда за ожиданием следует синхронный код?
Как было сказано на картинке выше, ожидание — это ожидание окончания выполнения асинхронного кода, за которым обычно следуют асинхронные функции, но что, если вы просто хотите следовать синхронному коду? Неважно, как видно на картинке выше, с кодом синхронизации,await 同步代码Он все равно будет конвертирован в Promise~