代码之家  ›  专栏  ›  技术社区  ›  user656925

概念-提炼承诺是如何运作的?

  •  12
  • user656925  · 技术社区  · 12 年前

    我看了很多实现,它们看起来都很不一样,我无法真正提炼出承诺的本质是什么。

    如果非要我猜测的话,它只是一个在回调触发时运行的函数。

    有人能在几行代码中实现最基本的承诺吗。

    例如,从这个 answer

    代码段1

    var a1 = getPromiseForAjaxResult(ressource1url);
    a1.then(function(res) {
        append(res);
        return a2;
    });
    

    函数是如何传递给的 then 知道什么时候该跑。

    也就是说,它是如何传递回ajax在完成时触发的回调代码的。

    代码段2

    // generic ajax call with configuration information and callback function
    ajax(config_info, function() {
        // ajax completed, callback is firing.
    });
    

    这两个片段是如何关联的?

    猜测:

    // how to implement this
    
    (function () {
        var publik = {};
            _private;
        publik.then = function(func){
            _private = func;
        };
        publik.getPromise = function(func){
            // ??
        };
        // ??
    }())
    
    6 回复  |  直到 8 年前
        1
  •  27
  •   T.J. Crowder    6 年前

    从根本上说,promise只是一个对象,它有一个标志,说明它是否已被解决,以及它维护的一系列函数,以在解决时通知它。代码有时可以说的不仅仅是单词,所以这里有一个 非常基本,不是真实的世界 示例纯粹是为了帮助传达概念:

    // See notes following the code for why this isn't real-world code
    function Promise() {
        this.settled = false;
        this.settledValue = null;
        this.callbacks = [];
    }
    Promise.prototype.then = function(f) {
        if (this.settled) {
            f(this.settledValue);                // See notes 1 and 2
        } else {
            this.callbacks.push(f);
        }
                                                 // See note 3 about `then`
                                                 // needing a return value
    };
    Promise.prototype.settle = function(value) { // See notes 4 and 5
        var callback;
    
        if (!this.settled) {
            this.settled = true;
            this.settledValue = value;
            while (this.callbacks.length) {
                callback = this.callbacks.pop();
                callback(this.settledValue);      // See notes 1 and 2
            }
        }
    };
    

    所以 Promise 保存状态,以及在约定完成时要调用的函数。的行为 沉淀 承诺通常是外部的 许诺 对象本身(当然,这取决于实际的使用,您可以将它们组合起来——例如,与jQuery的 ajax [ jqXHR ]对象)。

    同样,以上内容纯粹是概念性的,缺少了在任何现实世界的承诺实现中都必须存在的几个重要内容,这些内容才会有用:

    1. then settle 应该 总是 异步调用回调,即使promise已经解决。 然后 应该,因为否则调用者不知道回调是否是异步的。 解决 应该,因为回调应该在之后才运行 解决 已返回。(ES2015的承诺做到了这两件事。jQuery的 Deferred 没有。)

    2. 然后 解决 应确保回调中的失败(例如,异常)不会直接传播到代码调用 然后 解决 这与上面的#1部分相关,并且与下面的#3更相关。

    3. 然后 应返回 promise基于调用回调的结果(当时或以后)。这对于构成承诺化操作是相当基本的,但会使上述操作明显复杂化。任何合理的承诺实施都是如此。

    4. 我们需要不同类型的“结算”操作:“解析”(基本操作成功)和“拒绝”(失败)。有些用例可能有更多的状态,但已解决和已拒绝是基本的两个状态。(ES2015的承诺有决心也有拒绝。)

    5. 我们可能会 解决 (或单独的 resolve reject )在某种程度上是私有的,这样只有承诺的创建者才能解决它。(ES2015承诺和其他几个承诺通过 许诺 构造函数接受一个回调,该回调接收 决定 拒绝 作为参数值,因此只有回调中的代码才能解析或拒绝[除非回调中的编码以某种方式公开它们]。)

    等等。

        2
  •  15
  •   Bergi    4 年前

    有人能用几句话实现最基本的承诺吗?

    它在这里:

    function Promise(exec) {
        // takes a function as an argument that gets the fullfiller
        var callbacks = [], result;
        exec(function fulfill() {
            if (result) return;
            result = arguments;
            for (let c;c=callbacks.shift();)
                c.apply(null, arguments);
        });
        this.addCallback = function(c) {
            if (result)
                c.apply(null, result)
            else
                callbacks.push(c);
        }
    }
    

    附加的 then 带有链接(您将需要 the answer ):

    Promise.prototype.then = function(fn) {
        return new Promise(fulfill => {
            this.addCallback((...args) => {
                const result = fn(...args);
                if (result instanceof Promise)
                    result.addCallback(fulfill);
                else
                    fulfill(result);
            });
        });
    };
    

    这两个片段是如何关联的?

    ajax 从调用 getPromiseForAjaxResult 功能:

    function getPromiseForAjaxResult(ressource) {
        return new Promise(function(callback) {
            ajax({url:ressource}, callback);
        });
    }
    
        3
  •  1
  •   Keyvan    6 年前

    我已经在ES7中实现了一个。有了链接,就有70行了,如果算少的话。我认为状态机是实现承诺的正确范例。生成的代码比许多代码更容易理解 if s国际海事组织。在中详细描述 this article .

    这是代码:

    const states = {
        pending: 'Pending',
        resolved: 'Resolved',
        rejected: 'Rejected'
    };
    
    class Nancy {
        constructor(executor) {
            const tryCall = callback => Nancy.try(() => callback(this.value));
            const laterCalls = [];
            const callLater = getMember => callback => new Nancy(resolve => laterCalls.push(() => resolve(getMember()(callback))));
            const members = {
                [states.resolved]: {
                    state: states.resolved,
                    then: tryCall,
                    catch: _ => this
                },
                [states.rejected]: {
                    state: states.rejected,
                    then: _ => this,
                    catch: tryCall
                },
                [states.pending]: {
                    state: states.pending,
                    then: callLater(() => this.then),
                    catch: callLater(() => this.catch)
                }
            };
            const changeState = state => Object.assign(this, members[state]);
            const apply = (value, state) => {
                if (this.state === states.pending) {
                    this.value = value;
                    changeState(state);
                    for (const laterCall of laterCalls) {
                        laterCall();
                    }
                }
            };
    
            const getCallback = state => value => {
                if (value instanceof Nancy && state === states.resolved) {
                    value.then(value => apply(value, states.resolved));
                    value.catch(value => apply(value, states.rejected));
                } else {
                    apply(value, state);
                }
            };
    
            const resolve = getCallback(states.resolved);
            const reject = getCallback(states.rejected);
            changeState(states.pending);
            try {
                executor(resolve, reject);
            } catch (error) {
                reject(error);
            }
        }
    
        static resolve(value) {
            return new Nancy(resolve => resolve(value));
        }
    
        static reject(value) {
            return new Nancy((_, reject) => reject(value));
        }
    
        static try(callback) {
            return new Nancy(resolve => resolve(callback()));
        }
    }
    
        4
  •  0
  •   neatsu    8 年前

    这里有一个轻量级的promise实现,称为“sequence”,我在日常工作中使用它:

    (function() {
        sequence = (function() {
            var chained = [];
            var value;
            var error;
    
            var chain = function(func) {
                chained.push(func);
                return this;
            };
    
            var execute = function(index) {
                var callback;
                index = typeof index === "number" ? index : 0;
    
                if ( index >= chained.length ) {
                    chained = [];
                    return true;
                }
    
                callback = chained[index];
    
                callback({
                    resolve: function(_value) {
                        value = _value;
                        execute(++index);
                    },
                    reject: function(_error) {
                        error = _error;
                        execute(++index);
                    },
                    response: {
                        value: value,
                        error: error
                    }
                });
            };
    
            return {
                chain: chain,
                execute: execute
            };
        })();
    })();
    

    初始化后,您可以按以下方式使用序列:

    sequence()
        .chain(function(seq) {
            setTimeout(function() {
                console.log("func A");
                seq.resolve();
            }, 2000);
        })
        .chain(function(seq) {
            setTimeout(function() {
                console.log("func B");
            }, 1000)
        })
        .execute()
    

    要启用实际的链接,您需要调用seq对象的resolve()函数,回调必须将其用作参数。

    序列公开两个公共方法:

    • chain-这个方法只需将回调推送到一个私有数组
    • execute-此方法使用递归来实现回调的正确顺序执行。它基本上是按照链接它们的顺序执行回调,方法是将seq对象传递给每个回调。一旦当前回调被解析/拒绝,就执行下一次回调。

    “execute”方法是魔法发生的地方。它将“seq”对象传递给您的所有回调。因此,当您调用seq.resolve()或seq.rejectt()时,您实际上会调用下一个链式回调。

    请注意,此实现仅存储来自先前执行的回调的响应。

    有关更多示例和文档,请参阅: https://github.com/nevendyulgerov/sequence

        5
  •  0
  •   Edward    7 年前

    这里有一个简单的Promise实现,对我来说很有效。

        function Promise(callback) {
            this._pending = [];
            this.PENDING = "pending";
            this.RESOLVED = "resolved";
            this.REJECTED = "rejected";
            this.PromiseState = this.PENDING;
            this._catch = function (error) {
                console.error(error);
            };
            setTimeout(function () {
                try {
                    callback.call(this, this.resolve.bind(this), this.reject.bind(this));
                } catch (error) {
                    this.reject(error);
                }
            }.bind(this), 0)
        };
        Promise.prototype.resolve = function (object) {
            if (this.PromiseState !== this.PENDING) return;
            while (this._pending.length > 0) {
                var callbacks = this._pending.shift();
                try {
                    var resolve = callbacks.resolve;
                    if (resolve instanceof Promise) {
                        resolve._pending = resolve._pending.concat(this._pending);
                        resolve._catch = this._catch;
                        resolve.resolve(object);
                        return resolve;
                    }
                    object = resolve.call(this, object);
                    if (object instanceof Promise) {
                        object._pending = object._pending.concat(this._pending);
                        object._catch = this._catch;
                        return object;
                    }
                } catch (error) {
                    (callbacks.reject || this._catch).call(this, error);
                    return;
                }
            }
            this.PromiseState = this.RESOLVED;
            return object;
        };
        Promise.prototype.reject = function (error) {
            if (this.PromiseState !== this.PENDING) return;
            this.PromiseState = this.REJECTED;
            try {
                this._catch(error);
            } catch (e) {
                console.error(error, e);
            }
        };
        Promise.prototype.then = function (onFulfilled, onRejected) {
            onFulfilled = onFulfilled || function (result) {
                return result;
            };
            this._catch = onRejected || this._catch;
            this._pending.push({resolve: onFulfilled, reject: onRejected});
            return this;
        };
        Promise.prototype.catch = function (onRejected) {
            // var onFulfilled = function (result) {
            //     return result;
            // };
            this._catch = onRejected || this._catch;
            // this._pending.push({resolve: onFulfilled, reject: onRejected});
            return this;
        };
        Promise.all = function (array) {
            return new Promise(function () {
                var self = this;
                var counter = 0;
                var finishResult = [];
    
                function success(item, index) {
                    counter++;
                    finishResult[index] = item;
                    if (counter >= array.length) {
                        self.resolve(finishResult);
                    }
                }
                for(var i in array) {
                    var item = array[i];
                    if (item instanceof Promise) {
                        item.then(function (result) {
                            success(result,this);
                        }.bind(i), function (error) {
                            array.map(function (item) {
                                item.PromiseState = Promise.REJECTED
                            });
                            self._catch(error);
                        })
                    } else {
                        success(item, i);
                    }
                }
            });
        };
        Promise.race = function (array) {
            return new Promise(function () {
                var self = this;
                var counter = 0;
                var finishResult = [];
                array.map(function (item) {
                    if (item instanceof Promise) {
                        item.then(function (result) {
                            array.map(function (item) {
                                item.PromiseState = Promise.REJECTED
                            });
                            self.resolve(result);
                        }, function (error) {
                            array.map(function (item) {
                                item.PromiseState = Promise.REJECTED
                            });
                            self._catch(error);
                        })
                    } else {
                        array.map(function (item) {
                            item.PromiseState = Promise.REJECTED
                        });
                        self.resolve(item);
                    }
                })
            });
        };
        Promise.resolve = function (value) {
            return new Promise(function (resolve, reject) {
                try {
                    resolve(value);
                } catch (error) {
                    reject(error);
                }
            });
        };
        Promise.reject = function (error) {
            return new Promise(function (resolve, reject) {
                reject(error);
            });
        }
    

    讨论 here . 小提琴: here .

        6
  •  -1
  •   isnvi23h4    6 年前

    这是promise体系结构的绝对最小值

    function Promise(F) {
      var gotoNext = false;
      var stack = [];
      var args = [];
    
      var isFunction = function(f) {
        return f && {}.toString.call(f) === '[object Function]';
      };
    
      var getArguments = function(self, _args) {
        var SLICE = Array.prototype.slice;
    
        _args = SLICE.call(_args);
        _args.push(self);
    
        return _args;
      };
    
      var callNext = function() {
        var method = stack.shift();
    
        gotoNext = false;
        if (isFunction(method)) method.apply(null, args);
      };
    
      var resolve = [(function loop() {
        if (stack.length) setTimeout(loop, 0);
        if (gotoNext) callNext();
      })];
    
      this.return = function() {
        gotoNext = true;
        args = getArguments(this, arguments);
        if(resolve.length) resolve.shift()();
    
        return this;
      };
    
      this.then = function(fn) {
        if (isFunction(fn)) stack.push(fn);
    
        return this;
      };
    
      return this.then(F).return();
    }
    
    
    // --- below is a working implementation --- //
    
    var bar = function(p) {
      setTimeout(function() {
        console.log("1");
        p.return(2);
      }, 1000);
    };
    
    var foo = function(num, p) {
      setTimeout(function() {
        console.log(num);
        p.return(++num);
      }, 1000);
    };
    
    new Promise(bar)
      .then(foo)
      .then(foo)
      .then(foo);