作者eight0 (人类)
看板Ajax
标题Re: [问题] 同步变数接异步函数返回值
时间Sun Mar 12 21:19:44 2017
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。
--
(* ̄▽ ̄)/‧★*"`'*-.,_,.-*'`"*-.,_☆,.-*`
http://i.imgur.com/oAd97.png
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 118.166.131.180
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Ajax/M.1489324789.A.266.html
1F:推 Sunal: 阿~~~头好痛 03/12 21:46
2F:→ dannypsnl: 就是非同步为了确保程式正确按顺序执行就要一个callbac 03/12 23:42
3F:→ dannypsnl: k包着一个,非常的有事(咦 03/12 23:42
4F:→ dannypsnl: 重点是很难一眼看懂,Promise只是把这件事变成一个链结 03/12 23:42
5F:→ dannypsnl: ,但本质还是上面那样 03/12 23:42
6F:推 ZAbird: 完全看懂了!!而且也会实际上的操作跟想法 03/13 01:33
7F:→ ZAbird: 真的非常非常感谢你用心回我 帮我建立观念 03/13 01:33
8F:→ ZAbird: 其他语言写习惯了 用js真的是又崩溃又有趣XD 03/13 01:33
9F:→ ZAbird: async/await 看起来真的精简很多, 但支援度目前还不高? 03/13 01:35
10F:推 cliffk321: 推用心 03/13 02:32
11F:→ eight0: 对於支援度方面,可以参考 Babel/Buble 等的转译器 03/13 07:32
13F:→ ssccg: 同步接异步不可能是因为js的event loop架构,在其他语言一 03/13 09:49
14F:→ ssccg: 样有这套async机制,但是真的要接回sync就把原thread block 03/13 09:51
15F:→ ssccg: 即可,但在js中原则上不能也不应该这样做 03/13 09:51
16F:推 wotupset: 用心推 03/15 19:34