es6-promisify学习

167 阅读2分钟

前言

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。 这是源码共读的第14期,链接:juejin.cn/post/708315…

简介

将回调函数转换为Promises函数

使用

让我们用官网案例简单了解一下

const {promisify} = require("es6-promisify");

const fs = require("fs");
// 调用promisify返回一个新函数
const stat = promisify(fs.stat);

// Now usable as a promise!
try {
    // 可以直接用await 接收返回值
    const stats = await stat("example.txt");
    console.log("Got stats", stats);
} catch (err) {
    console.error("Yikes!", err);
}

源码

  1. promisify接收一个original函数传参,返回一个函数function, 在返回的这个function里面包裹一个Promise对象,并将Promise的resove函数和reject函数裹在callback函数中,再把callback函数传参给original函数内部调用, 拿到值后resove结果
  2. original缓存到promisify函数作用域中,promisify方法再返回一个函数,函数内部又调用original,形成了一个闭包
const customArgumentsToken = "__ES6-PROMISIFY--CUSTOM-ARGUMENTS__";

/**
 * promisify()
 * 转换基于回调的函数 -- func(arg1, arg2 .. argN, callback) --
 * 为ES6-compatible Promise. Promisify 提供了一个默认回调函数形式 (error, result)
 * @param {function} original - 将要转化为promisify函数的function
 * @return {function} A promisified version of `original`
 */
export function promisify(original) {
    // original判断,
    if (typeof original !== "function") {
        throw new TypeError("Argument to promisify must be a function");
    }

    const argumentNames = original[customArgumentsToken];

    // 存在用户自定义Promise,则优先获取
    const ES6Promise = promisify.Promise || Promise;

    // If we can find no Promise implemention, then fail now.
    if (typeof ES6Promise !== "function") {
        throw new Error("No Promise implementation found; do you need a polyfill?");
    }
    
    // 返回一个函数
    return function (...args) {
        return new ES6Promise((resolve, reject) => {
            // args数组里增加一个callback回调函数
            args.push(function callback(err, ...values) {
                if (err) {
                    return reject(err);
                }
                // values.length为1 或者没有argumentNames,直接resove
                if (values.length === 1 || !argumentNames) {
                    return resolve(values[0]);
                }
                
                // 存在自定义属性,则赋值在一个对象o中
                const o = {};
                values.forEach((value, index) => {
                    const name = argumentNames[index];
                    if (name) {
                        o[name] = value;
                    }
                });
                // resolve这个o对象
                resolve(o);
            });

            // 执行original函数, 将args传参传入,此时的args存在callback函数
            original.apply(this, args);
        });
    };
}

// 将argumentNames和Promise属性挂载到promisify函数上,这样用户可以直接使用
promisify.argumentNames = customArgumentsToken;
promisify.Promise = undefined;

promisify简单实现

const loadScript = (url, callback) => {
    const script = document.createElement('script')
    script.src = url
    script.onload = () => callback()
    script.onerror = () => callback(new Error(`Script load error for ${url}`))
}

const promisefy = (fn) => {
    return (...args) => {
        return new Promise((resolve, reject) => {
            args.push((error) => {
                if(error instanceof Error) return reject(error)
                resolve()
            })
            // 执行fn函数
            fn.apply(this, args)
        })
    }
}

const loads = promisefy(loadScript)
const res = await loads('https://example.com/script.js').catch(err => {
    console.log(err)
})

总结

对promise使用又熟悉了,我们可以把resove或者reject函数传参到自定义函数中实现各种逻辑,欢迎大家指出问题~完结撒花