我看过许多实现,它们看起来都很不同,我无法真正提炼出承诺的本质。
如果我不得不猜测它只是一个在回调触发时运行的函数。
有人可以通过几行代码实现最基本的承诺。
例如,来自此answer
Snippet 1
var a1 = getPromiseForAjaxResult(ressource1url);
a1.then(function(res) {
append(res);
return a2;
});
传递给then
的函数如何知道何时运行。
也就是说,它是如何传递回ajax在完成时触发的回调代码。
Snippet 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){
// ??
};
// ??
}())
答案 0 :(得分:25)
从根本上说,承诺只是一个对象,它有一个标记,说明它是否已经解决,以及它维护的一系列功能,以便在结算时通知。代码有时可以说不仅仅是单词,所以这里有一个非常基本的,不是真实世界的示例纯粹是为了帮助传达这些概念:
// 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
保持状态,以及在承诺结算时调用的函数。 解决承诺的行为通常在Promise
对象本身的外部(当然,这取决于实际使用,您可以将它们组合在一起 - 例如,与jQuery的{{ 1}} [ajax
]个对象)。
同样,上述内容纯粹是概念性的,并且缺少必须在任何现实世界的承诺实施中出现的几个重要事项,以使其有用:
jqXHR
和then
应该始终异步调用回调,即使承诺已经解决。 settle
应该是因为否则调用者不知道回调是否是异步的。 then
应该因为回调在settle
返回之后才会运行。 (ES2015承诺做这两件事.jQuery的settle
没有。)
Deferred
和then
应确保回调失败(例如,异常)不会直接传播到调用settle
或then
的代码。这部分与上面的#1相关,对于下面的#3更是如此。
settle
应根据调用回调的结果(当时或之后)返回新承诺。这对于编写承诺操作非常重要,但会使上述内容复杂化。任何合理的承诺都可以实现。
我们需要不同类型的“结算”操作:“解决”(基础操作成功)和“拒绝”(失败)。某些用例可能包含更多状态,但已解决和拒绝是最基本的两种状态。 (ES2015的承诺已经解决并拒绝。)
我们可能会以某种方式使then
(或单独的settle
和resolve
)私有,这样只有承诺的创建者才能解决它。 (ES2015承诺 - 以及其他几个 - 通过让reject
构造函数接受一个接收Promise
和resolve
作为参数值的回调来实现这一点,因此只有该回调中的代码才能解析或拒绝[除非回调中的代码以某种方式使它们公开]。)
等等。
答案 1 :(得分:10)
有人可以在几行中实现最基本的承诺吗?
这是:
function Promise(fn) {
// takes a function as an argument that gets the fullfiller
var callbacks = [], result;
fn(function fulfill() {
if (result) return;
result = arguments;
for (var 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) {
var that = this;
return new Promise(function(c){
that.addCallback(function() {
var result = fn.apply(null, arguments);
if (result instanceof Promise)
result.addCallback(c);
else
c(result);
});
});
};
从这两个片段有何关联?
ajax
函数调用 getPromiseForAjaxResult
:
function getPromiseForAjaxResult(ressource) {
return new Promise(function(callback) {
ajax({url:ressource}, callback);
});
}
答案 2 :(得分:1)
这是一个轻量级的承诺实现,称为“序列”,我在日常工作中使用它:
(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()函数,您的回调必须将其用作参数。
序列公开两种公共方法:
'执行'方法是魔术发生的地方。它将'seq'对象传递给所有回调。所以当你调用seq.resolve()或seq.reject()时,你实际上会调用下一个链式回调函数。
请注意,此实现仅存储来自先前执行的回调的响应。
有关更多示例和文档,请参阅: https://github.com/nevendyulgerov/sequence
答案 3 :(得分:0)
这是一个适合我的简单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);
});
}
答案 4 :(得分:0)
这是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);
答案 5 :(得分:0)
我已经在ES7中实现了一个。如果使用链接,则只有70条线。我认为State Machine是实现诺言的正确范例。产生的代码比许多if
的恕我直言更易于理解。在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()));
}
}