【问题标题】:How to implement a simpler Promise in JavaScript?如何在 JavaScript 中实现更简单的 Promise?
【发布时间】:2021-11-10 02:09:07
【问题描述】:

我正在学习 JavaScript,我决定在 JavaScript 中实现一个自定义的 Promise 类是一个很好的挑战。然后我设法实现了该方法,它工作得很好,但是我在错误处理和方法捕获方面遇到了困难。这是我的 Promise 类代码(在一个名为 Promise.mjs 的模块中):

export default class _Promise {
  constructor(executor) {
    if (executor && executor instanceof Function) {
      try {
        executor(this.resolve.bind(this), this.reject.bind(this));
      } catch (error) {
        this.reject(error);
      }
    }
  }
  resolve() {
    if (this.callback && this.callback instanceof Function) {
      return this.callback(...arguments);
    }
  }
  reject(error) {
    if (this.errorCallback && this.errorCallback instanceof Function) {
      return this.errorCallback(error);
    } else {
      throw `Unhandled Promise Rejection\n\tError: ${error}`;
    }
  }
  then(callback) {
    this.callback = callback;
    return this;
  }
  catch(errorCallback) {
    this.errorCallback = errorCallback;
    return this;
  }
}

当我在下面的代码中导入和使用这个类时,所有的 then() 子句都按照相应的方式运行,并且我在控制台中得到了想要的结果:

import _Promise from "./Promise.mjs";

function sum(...args) {
    let total = 0;
    return new _Promise(function (resolve, reject) {
        setTimeout(function () {
            for (const arg of args) {
                if (typeof arg !== 'number') {
                    reject(`Invalid argument: ${arg}`);
                }
                total += arg;
            }
            resolve(total);
        }, 500);
    });
}

console.time('codeExecution');
sum(1, 3, 5).then(function (a) {
    console.log(a);
    return sum(2, 4).then(function (b) {
        console.log(b);
        return sum(a, b).then(function (result) {
            console.log(result);
            console.timeEnd('codeExecution');
        });
    });
}).catch(function (error) {
    console.log(error);
});

但是,当我向 sum() 函数添加一个无效参数(即不是数字)时,reject() 方法会运行,但它不会像应该那样停止 then() 链,我们也得到一个例外。这可以从以下代码中看出:

import _Promise from "./Promise.mjs";

function sum(...args) {
    let total = 0;
    return new _Promise(function (resolve, reject) {
        setTimeout(function () {
            for (const arg of args) {
                if (typeof arg !== 'number') {
                    reject(`Invalid argument: ${arg}`);
                }
                total += arg;
            }
            resolve(total);
        }, 500);
    });
}

console.time('codeExecution');
sum(1, 3, '5').then(function (a) {
    console.log(a);
    return sum(2, 4).then(function (b) {
        console.log(b);
        return sum(a, b).then(function (result) {
            console.log(result);
            console.timeEnd('codeExecution');
        });
    });
}).catch(function (error) {
    console.log(error);
});

另外,如果我在嵌套的 then() 方法中发现错误,外部的 catch() 不会注意到这一点,我会再次遇到异常。目标是实现 Promises 的轻量级功能版本,但不一定具有所有功能。你能帮帮我吗?

【问题讨论】:

  • 您有什么困难,您需要什么帮助?您已将其范围缩小到 catch 方法,但在这方面,有什么问题?
  • 您在问题中提供的代码运行 - 也许如果您显示失败的代码,您可能会得到答案
  • 我认为问题在于当我们尝试将字符串添加到 sum 函数 (sum(1, 3, 5,'foo') 中时,我们不会破坏 then() 链。我们还会在不应该的时候抛出Unhandled Promise rejection。不过只是猜测。
  • 真的,问题在于这是一个幼稚的 Promises 实现 - 无意冒犯,但即使是最小的 Promises 正确实现也是这个大小的两倍。第一个问题是,.then 可以在一个承诺上被多次调用。其次,.then 接受两个参数,onResolvedonRejected - 问题询问“如何实现自定义 Promise” - 但是,代码与 Promise 完全不同 - promisesaplus.com

标签: javascript node.js promise


【解决方案1】:

您的代码中的问题是您的sum 函数同时调用了rejectresolve 函数。 sum 函数中没有任何处理会导致它最后不调用 resolve 函数,并且您的 _Promise 中没有任何内容可以阻止此行为。

您有 2 个选项来解决此问题。

选项 1 是,如果您希望您的 _Promise 表现得像真正的 Promise,您将需要管理一个状态,并且一旦一个承诺达到最终状态,就停止调用 callback errorCallback

选项 2 是防止在调用 _Promise 的函数(在本例中为 sum 函数)中同时调用 rejectresolve

【讨论】:

    【解决方案2】:

    使用你们提供给我的 cmets,我能够改进代码并纠正提到的错误,如下所示。现在,我希望您就如何继续和改进代码给我一些建议。谢谢。 (代码也可以在github找到)。

    const PENDING = 0;
    const FULFILLED = 1;
    const REJECTED = 2;
    
    function _Promise(executor) {
    
        let state = PENDING;
        let callOnFulfilled = [];
        let callOnRejected = undefined;;
    
        function resolve(...args) {
            if (!state) {
                state = FULFILLED;
            }
    
            resolveCallbacks(...args);
        };
        function reject(error) {
            state = REJECTED;
            if (callOnRejected && (callOnRejected instanceof Function)) {
                callOnRejected(error);
                callOnRejected = undefined;
                callOnFulfilled = [];
            } else {
                throw `Unhandled Promise Rejection\n\tError: ${error}`;
            }
        };
        function resolveCallbacks(...value) {
            if (state !== REJECTED) {
                let callback = undefined;
                do {
                    callback = callOnFulfilled.shift();
                    if (callback && (callback instanceof Function)) {
                        const result = callback(...value);
                        if (result instanceof _Promise) {
                            result.then(resolveCallbacks, reject);
                            return;
                        } else {
                            value = [result];
                        }
                    }
                } while (callback);
            }
        };
    
        if (executor && (executor instanceof Function)) {
            executor(resolve, reject);
        }
    
        this.then = function (onFulfilled, onRejected) {
            if (onFulfilled) {
                callOnFulfilled.push(onFulfilled);
                if (state === FULFILLED) {
                    resolveCallbacks();
                }
            }
            if (onRejected && !callOnRejected) {
                callOnRejected = onRejected;
            }
            return this;
        };
        this.catch = function (onRejected) {
            return this.then(undefined, onRejected);
        };
    }
    
    function sum(...args) {
        let total = 0;
        return new _Promise(function (resolve, reject) {
            setTimeout(function () {
                for (const arg of args) {
                    if (typeof arg !== 'number') {
                        reject(`Invalid argument: ${arg}`);
                    }
                    total += arg;
                }
                resolve(total);
            }, 500);
        });
    }
    
    console.time('codeExecution');
    sum(1, 3, 5).then(function (a) {
        console.log(a);
        return sum(2, 4).then(function (b) {
            console.log(b);
            return sum(a, b).then(function (result) {
                console.log(result);
                return 25;
            });
        }).then(function (value) {
            console.log(value);
            console.timeEnd('codeExecution');
        });
    }).catch(function (error) {
        console.log(error);
    });
    

    【讨论】:

    猜你喜欢
    • 2019-08-27
    • 1970-01-01
    • 2018-03-16
    • 1970-01-01
    • 2015-03-03
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多