Promise 无法解决你的问题,就如一楼所说,你必须把赋值的动作移到 callback
函数内。Promise 是一种用来解决多层 callback 的工具。
先了解 JavaScript 的 callback(回呼函数)。
JavaScript 中,有些异步函式会要求你给它一个 callback,如 setTimeout。
当操作完成的时候(对 setTimeout 来说,就是计时器时间到的时候),callback
就会被呼叫。以你的程式为例︰
var x;
function getValue(){
var temp; // temp 没有值,为 undefined
setTimeout(
function(){temp = 3}, // 这个函式在 2 秒后才会被呼叫
2000
);
return temp // 由于前述的原因,temp 还是 undefined
}
x = getValue(); // 由 getValue 回传的值,undefined
过了两秒后,setTimeout 内的 function 被呼叫,把 getValue 内的 temp 设为 3,
但这不影响外面的 x。如果要正确取得两秒后 getValue 的回传值,你也必须把
getValue 也设计成使用 callback 的型式︰
var x;
function getValue(done) { // done 是外部传来的 callback
var temp;
setTimeout(
function(){
temp = 3;
done(temp); // 在完成操作后呼叫 callback,并把值传进去
},
2000
);
}
getValue(function(value) { // getValue 已经变成异步函式了,这个 function
// 会在 getValue 操作完成时被呼叫
x = value;
console.log(x) // x 已被赋值,可以对 x 操作
});
如此一来,getValue 也变成了一个异步函数,和 setTimeout 一样。
问题来了,假如你要多次使用异步函数,callback 就会变成很多层……
var x, y;
// 省略 getValue 的定义,和上方相同
getValue(function(value) {
x = value; // 以异步(callback)获得 getValue 的回传值后,再呼叫一次
// 以获得第二个值
getValue(function(value) {
y = value; // 获得第二个值
console.log(x, y); // 这时 x, y 都已经取得了,可以开始你想要的操作
});
});
那呼叫 100 次岂不是要敲 tab 键(缩排)一百次?于是有人想到了用 queue 来储存
callback,再依序呼叫︰
var x, y;
function queueAsyncCaller(queue) { // 接受一个异步函数阵列
function dequeue() {
var asyncFunction = queue.shift(); // 依序执行
if (asyncFunction) {
asyncFunction(dequeue); // 当 asyncFunction 操作完成时,会进行下一次的
// dequeue
}
}
dequeue();
}
queueAsyncCaller([
function(done) {
getValue(function(value) { // 所有的 getValue 都在异步函数中执行
x = value;
done();
});
},
function(done) {
getValue(function(value) {
y = value;
done();
});
},
function() {
console.log(x, y);
}
]);
如此一来,一层层的 callback 就被“摊平”成一个 callback“炼”。
Promise 就是建立在这种思想上的。但是
* Promise 的 callback 不是在一开始以 array 储存,而是以 .then() 动态新增
* Promise 的 callback (.then) 可以回传一个 Promise,也就是说 Promise 炼
可以动态新增
* Promise 并非只接受 done() 一个 callback,而可以根据状况 resolve() 或
reject()
如果把 getValue 以 Promise 改写的话︰
function getValue() { // getValue 现在不接受 callback
// 而是回传一个 Promise 物件,可以用 .then 动态新增 callback
return new Promise(function(resolve, reject) {
var temp;
setTimeout(function() {
temp = 3;
resolve(temp); // 两秒后操作完成,将回传值送给 resolve()
}, 2000);
});
}
var x, y;
getValue() // 建立 getValue Promise
.then(function(value) { // 设定 callback
x = value;
return getValue(); // 再插入一个 getValue 进 Promise 炼
})
.then(function(value) { // 设定 callback
y = value;
})
.then(function() {
console.log(x, y);
});
还要再精简的话,可以使用 async/await(前几天的 Firefox 52 已经可以用了),
把 callback 合并进同个 context︰
// async 函式呼叫时会回传 Promise
async function getValues() {
var x, y;
x = await getValue(); // await 会使 context 暂停,等待右方的 Promise 回
// 传结果
y = await getValue();
console.log(x, y);
// 函式结束时,Promise 被 resolve
}
getValues();
但是“同步函数接异步函数返回值”仍然是不可能的,
getValues 本身是异步函数,回传 Promise。