Re: [问题] 同步变量接异步函数返回值 callback/promise

楼主: eight0 (欸XD)   2017-03-12 21:19:44
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。
作者: Sunal (SSSSSSSSSSSSSSSSSSSSSSS)   2017-03-12 21:46:00
阿~~~头好痛
作者: dannypsnl (秦书)   2017-03-12 23:42:00
就是异步为了确保程式正确按顺序执行就要一个callback包著一个,非常的有事(咦重点是很难一眼看懂,Promise只是把这件事变成一个链结,但本质还是上面那样
作者: ZAbird (炸鸟)   2017-03-13 01:33:00
完全看懂了!!而且也会实际上的操作跟想法真的非常非常感谢你用心回我 帮我建立观念其他语言写习惯了 用js真的是又崩溃又有趣XDasync/await 看起来真的精简很多, 但支援度目前还不高?
作者: cliffk321 (Cliff)   2017-03-13 02:32:00
推用心
楼主: eight0 (欸XD)   2017-03-13 07:32:00
对于支援度方面,可以参考 Babel/Buble 等的转译器https://babeljs.io/ https://buble.surge.sh/guide/
作者: ssccg (23)   2017-03-13 09:49:00
同步接异步不可能是因为js的event loop架构,在其他语言一样有这套async机制,但是真的要接回sync就把原thread block即可,但在js中原则上不能也不应该这样做
作者: wotupset (wotupset)   2017-03-15 19:34:00
用心推

Links booklink

Contact Us: admin [ a t ] ucptt.com