1. Introducción
-
Este artículo participó en la actividad semanal de colectura del código fuente iniciada por la cuenta pública @朝川视频, haga clic para obtener detalles y participar juntos.
-
Este es el cuarto número de lectura de código fuente, enlace: código fuente co
2. Conocimientos básicos
co se usa para convertir la función Generador en una Promesa y ejecutarla, de modo que pueda async、await
ejecutarse automáticamente como una función.
2.1 Generador
2.2 Casos de uso del generador
Consulte el análisis del código fuente en el artículo de Ruochuan y escriba uno por imitación.
- Ejecución del generador
// 模拟请求
function request(ms = 1000) {
return new Promise(resolve => {
setTimeout(() => {
resolve(111)
}, ms)
})
}
// 生成器函数
function* generatorFunc() {
const res = yield request()
console.log('打印***res', res)
}
generatorFunc()
Obtendrá one 迭代器对象
, y obtendrá el resultado solo llamando al siguiente método, y no obtendrá el resultado si no lo ejecuta. Entonces, ¿puede escribir una herramienta de ejecución automática para completar esta función?
- Primera versión - ejecución automática de rendimiento único
function request(ms = 1000) {
return new Promise(resolve => {
setTimeout(() => {
resolve(111)
}, ms)
})
}
function* generatorFunc() {
const res = yield request()
console.log('打印***res', res)
}
function coSimple(gen) {
gen = gen()
console.log('打印***gen', gen)
const ret = gen.next()
const promise = ret.value
promise.then(res => {
gen.next(res)
})
}
coSimple(generatorFunc)
Use next para llamar, envuelva el resultado en una promesa y use gen.next(res) para devolver el resultado de la ejecución.Si no se llama a next, el res después del rendimiento de la función del generador no está definido.
- Segunda versión: considere rendimientos múltiples y paso de parámetros
function* generatorFunc2(suffix=''){
const res = yield request()
console.log('打印***res-1'+suffix,res)
const res2 = yield request()
console.log('打印***res-2'+suffix,res2)
}
function coSimple2(gen){
const ctx = this
const args = Array.prototype.slice.call(arguments,1)// 转成数组
gen = gen.apply(ctx,args)
console.log('打印***gen',gen)
// 第一个调用
const ret = gen.next()
console.log('打印***ret',ret)
const promise = ret.value
promise.then(res=>{
console.log('打印***res',res)
const ret = gen.next(res)// 此处不传入,则yield后面读取不到res,111
const promise = ret.value
promise.then(res=>{
gen.next(res)
})
})
}
coSimple2(generatorFunc2,'888')
Aquí, se anidan varias llamadas de rendimiento y el valor se obtiene manualmente cada vez. Entonces puede ser automático y manual, hasta el final.
- Versión 3 - Rendimiento ilimitado
let index = 0
function request(ms = 1000) {
return new Promise(resolve => {
setTimeout(() => {
resolve(index++)
}, ms)
})
}
function* generatorFunc3(suffix=''){
const res = yield request()
console.log('打印***res-1'+suffix,res)
const res2 = yield request()
console.log('打印***res-2'+suffix,res2)
const res3 = yield request()
console.log('打印***res-3'+suffix,res3)
const res4 = yield request()
console.log('打印***res-4'+suffix,res4)
}
function coSimple3(gen){
const ctx = this
const args = Array.prototype.slice.call(arguments,1)
gen = gen.apply(ctx,args)
console.log('打印***gen',gen)
return new Promise(()=>{
function onFulfilled(res){
const ret = gen.next(res)
console.log('打印***ret',ret)
next(ret)
}
function next(ret){
const promise = ret.value
promise&&promise.then(onFulfilled)
}
onFulfilled()
})
}
coSimple3(generatorFunc3)
Se modificó la solicitud para que sea más fácil ver el paso de cada parámetro.
Devuelva la promesa, defina onFulfilled, llame manualmente a next para ejecutar la solicitud, use next para obtener el último resultado y se llame a sí mismo hasta que se ejecuten todos los rendimientos.
3. Análisis del código fuente
3.1 Método de función
slice
: se utiliza para crear unaArray.prototype.slice
referencia a un archivo .co
: Función principal, convertir la función Generador o el objeto Generador en Promesa y ejecutar.co.wrap
: Envuelve la función de Generador dada en una función que devuelve una Promesa.toPromise
:yield
Convierte el valor en una Promesa.thunkToPromise
: Convierte un thunk (una función que acepta una devolución de llamada) en una Promesa.arrayToPromise
: Convierte una matriz que contiene múltiples "rendibles" en una Promesa.objectToPromise
: Convierte un objeto que contiene múltiples "rendibles" en una Promesa.isPromise
: Comprueba si un objeto es una Promesa.isGenerator
: Comprueba si un objeto es un Generador (tienenext
ythrow
métodos).isGeneratorFunction
: comprueba si una función es una función de generador.isObject
: Comprueba si un valor es un objeto simple.
3.2 co función
function co(gen) {
var ctx = this; // 保存当前上下文
var args = slice.call(arguments, 1); // 获取除了第一个参数(gen)之外的其他参数
// 我们将所有内容包装在一个 Promise 中,以避免 Promise 链式调用导致的错误。
return new Promise(function(resolve, reject) {
if (typeof gen === 'function') gen = gen.apply(ctx, args); // 如果 gen 是函数,则执行函数并将结果赋值给 gen
if (!gen || typeof gen.next !== 'function') return resolve(gen); // 如果 gen 不是函数,或者不具有 next 方法,则直接返回结果为 gen 的 Promise
onFulfilled();
// 当前步骤执行成功时的处理函数
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res); // 执行生成器的 next 方法,并将结果赋值给 ret
} catch (e) {
return reject(e); // 如果出现异常则将异常作为 Promise 的拒绝理由
}
next(ret); // 继续执行下一步
return null;
}
// 当前步骤执行失败时的处理函数
function onRejected(err) {
var ret;
try {
ret = gen.throw(err); // 执行生成器的 throw 方法,并将结果赋值给 ret
} catch (e) {
return reject(e); // 如果出现异常则将异常作为 Promise 的拒绝理由
}
next(ret); // 继续执行下一步
}
// 获取生成器的下一个值,并返回一个 Promise
function next(ret) {
if (ret.done) return resolve(ret.value); // 如果生成器完成,则将生成器的结果作为 Promise 的解决值
var value = toPromise.call(ctx, ret.value); // 将生成器的返回值转换为 Promise
if (value && isPromise(value)) return value.then(onFulfilled, onRejected); // 如果返回值是 Promise,则等待 Promise 的状态并执行相应的处理函数
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"')); // 如果生成器的返回值不是函数、Promise、Generator、数组或对象,则拒绝 Promise
}
});
}
En co
la función, primero juzgará si el parámetro entrante gen
es una función de Generador y, de ser así, ejecutará la función para obtener el objeto Generador. Luego, ejecute el método del objeto Generador en una nueva Promesa next
y realice el procesamiento correspondiente de acuerdo con el resultado devuelto hasta que se complete el Generador ( done
verdadero), y finalmente devuelva el resultado de la Promesa.
4. Resumen
Al estudiar co
el código fuente, aprendí:
yield
Cómo escribir código asíncrono usando funciones de generador y la palabra clave.next()
Cómo usar los métodos y de las funciones del generadorthrow()
para controlar el flujo de ejecución de los generadores.- Cómo manejar el valor de retorno de una operación asincrónica.
co
La función pasa el valor de retorno de la operación asincrónica como argumento a layield
expresión de la función generadora y suspende el flujo de ejecución de la función generadora hasta la siguiente llamada alnext()
método.
Aprender juntos y crecer juntos. O ^ O