предисловие
Если бы вас попросили вручную написать реализацию асинхронных функций, сочли бы вы это сложным? Эта статья расскажет вам о сути всего в 20 строках.
Часто говорят, что асинхронная функция — это синтаксический сахар функции-генератора, так что же это за сахар? Давайте снимем с него глазурь слой за слоем.
Некоторые студенты хотят сказать, что если используется функция генератора, зачем реализовывать асинхронность?
Цель этой статьи — помочь всем понять, как асинхронность и генератор работают вместе для управления асинхронностью.
Пример
const getData = () => new Promise(resolve => setTimeout(() => resolve("data"), 1000))
async function test() {
const data = await getData()
console.log('data: ', data);
const data2 = await getData()
console.log('data2: ', data2);
return 'success'
}
// 这样的一个函数 应该再1秒后打印data 再过一秒打印data2 最后打印success
test().then(res => console.log(res))
идеи
Что, если мы представим это в виде функции-генератора в этом простом случае?
function* testG() {
// await被编译成了yield
const data = yield getData()
console.log('data: ', data);
const data2 = yield getData()
console.log('data2: ', data2);
return 'success'
}
Мы знаем, что функция-генератор не будет выполняться автоматически, каждый раз, когда будет вызываться ее следующий метод, она будет оставаться на следующей позиции yield.
Используя эту функцию, мы можем заставить эту функцию генератора полностью реализовать функцию асинхронной функции, если мы напишем автоматически выполняемую функцию.
const getData = () => new Promise(resolve => setTimeout(() => resolve("data"), 1000))
var test = asyncToGenerator(
function* testG() {
// await被编译成了yield
const data = yield getData()
console.log('data: ', data);
const data2 = yield getData()
console.log('data2: ', data2);
return 'success'
}
)
test().then(res => console.log(res))
Итак, общая идея определена,
asyncToGenerator
принять одинgenerator
Функция, которая возвращаетpromise
,
Ключ в том, что вyield
Как автоматически выполнить асинхронный процесс, который разделен.
Если выполнять вручную
Прежде чем писать эту функцию, давайте смоделируем ручной вызов этой функции.generator
Функция выполняет процесс шаг за шагом, что полезно для дальнейшего обдумывания.
function* testG() {
// await被编译成了yield
const data = yield getData()
console.log('data: ', data);
const data2 = yield getData()
console.log('data2: ', data2);
return 'success'
}
Мы сначала звонимtestG
создать итератор
// 返回了一个迭代器
var gen = testG()
Затем начните выполнение в первый разnext
// 第一次调用next 停留在第一个yield的位置
// 返回的promise里 包含了data需要的数据
var dataPromise = gen.next()
здесь возвращаетсяpromise
, первый разgetData()
вернулсяpromise
,Уведомление
const data = yield getData()
Этот код следует разделить на две части, левую и правую, первый вызовnext
, на самом деле он просто осталсяyield getData()
здесь,
data
стоимость не определена.
Итак, когда будет определена ценность данных?
В следующий раз, когда вы вызовете next, переданный параметр будет использоваться как значение, принятое до предыдущего yield.
То есть снова звонимgen.next('这个参数才会被赋给data变量')
когда
data
значение будет определяться как'这个参数才会被赋给data变量'
gen.next('这个参数才会被赋给data变量')
// 然后这里的data才有值
const data = yield getData()
// 然后打印出data
console.log('data: ', data);
// 然后继续走到下一个yield
const data2 = yield getData()
Затем спускайтесь вниз, пока не встретите следующуюyield
, продолжайте процесс так...
Это сложный момент в разработке функции-генератора, но для достижения нашей цели нам все равно придется его изучить~
С помощью этой функции, если мы таким образом контролируем процесс yield, можем ли мы добиться асинхронной сериализации?
function* testG() {
// await被编译成了yield
const data = yield getData()
console.log('data: ', data);
const data2 = yield getData()
console.log('data2: ', data2);
return 'success'
}
var gen = testG()
var dataPromise = gen.next()
dataPromise.then((value1) => {
// data1的value被拿到了 继续调用next并且传递给data
var data2Promise = gen.next(value1)
// console.log('data: ', data);
// 此时就会打印出data
data2Promise.value.then((value2) => {
// data2的value拿到了 继续调用next并且传递value2
gen.next(value2)
// console.log('data2: ', data2);
// 此时就会打印出data2
})
})
Такой похожcallback hell
Вызов позволяет нашей функции-генератору четко организовать асинхронное расположение.
выполнить
С этой линейкой мыслей, для достижения этих функций высшего порядка становится очень простым.
Давайте посмотрим на структуру в целом и получим впечатление, а затем объясним ее построчно.
function asyncToGenerator(generatorFunc) {
return function() {
const gen = generatorFunc.apply(this, arguments)
return new Promise((resolve, reject) => {
function step(key, arg) {
let generatorResult
try {
generatorResult = gen[key](arg)
} catch (error) {
return reject(error)
}
const { value, done } = generatorResult
if (done) {
return resolve(value)
} else {
return Promise.resolve(value).then(val => step('next', val), err => step('throw', err))
}
}
step("next")
})
}
}
Не больше, не меньше, 22 строки.
Ниже приводится построчное объяснение.
function asyncToGenerator(generatorFunc) {
// 返回的是一个新的函数
return function() {
// 先调用generator函数 生成迭代器
// 对应 var gen = testG()
const gen = generatorFunc.apply(this, arguments)
// 返回一个promise 因为外部是用.then的方式 或者await的方式去使用这个函数的返回值的
// var test = asyncToGenerator(testG)
// test().then(res => console.log(res))
return new Promise((resolve, reject) => {
// 内部定义一个step函数 用来一步一步的跨过yield的阻碍
// key有next和throw两种取值,分别对应了gen的next和throw方法
// arg参数则是用来把promise resolve出来的值交给下一个yield
function step(key, arg) {
let generatorResult
// 这个方法需要包裹在try catch中
// 如果报错了 就把promise给reject掉 外部通过.catch可以获取到错误
try {
generatorResult = gen[key](arg)
} catch (error) {
return reject(error)
}
// gen.next() 得到的结果是一个 { value, done } 的结构
const { value, done } = generatorResult
if (done) {
// 如果已经完成了 就直接resolve这个promise
// 这个done是在最后一次调用next后才会为true
// 以本文的例子来说 此时的结果是 { done: true, value: 'success' }
// 这个value也就是generator函数最后的返回值
return resolve(value)
} else {
// 除了最后结束的时候外,每次调用gen.next()
// 其实是返回 { value: Promise, done: false } 的结构,
// 这里要注意的是Promise.resolve可以接受一个promise为参数
// 并且这个promise参数被resolve的时候,这个then才会被调用
return Promise.resolve(
// 这个value对应的是yield后面的promise
value
).then(
// value这个promise被resove的时候,就会执行next
// 并且只要done不是true的时候 就会递归的往下解开promise
// 对应gen.next().value.then(value => {
// gen.next(value).value.then(value2 => {
// gen.next()
//
// // 此时done为true了 整个promise被resolve了
// // 最外部的test().then(res => console.log(res))的then就开始执行了
// })
// })
function onResolve(val) {
step("next", val)
},
// 如果promise被reject了 就再次进入step函数
// 不同的是,这次的try catch中调用的是gen.throw(err)
// 那么自然就被catch到 然后把promise给reject掉啦
function onReject(err) {
step("throw", err)
},
)
}
}
step("next")
})
}
}
Адрес источника
этоjs-файлКод можно напрямую ввести в браузер для запуска, добро пожаловать на флирт.
Суммировать
В этой статье простейшим образом реализована функция asyncToGenerator, которая является ядром компиляции асинхронных функций в Babel.Конечно, в Babel функция-генератор также компилируется в очень примитивный вид.В этой статье мы напрямую заменяем ее на генератор .
Это также отличный шаблон для реализации сериализации промисов.Если эта статья была вам полезна, пожалуйста, оцените ее.
❤️Спасибо всем
1. Если эта статья была вам полезна, пожалуйста, поддержите ее лайком, ваш "лайк" - движущая сила моего творчества.
2. Обратите внимание на лицевую часть официального аккаунта от продвинутого до допуска! Время от времени присылайте качественные оригинальные статьи.