※ 本文為 terievv 轉寄自 ptt.cc 更新時間: 2017-03-15 19:29:10
看板 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 的 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
※ 文章代碼(AID): #1OnKhr9c (Ajax)
※ 文章網址: https://www.ptt.cc/bbs/Ajax/M.1489324789.A.266.html
推 : 阿~~~頭好痛1F 03/12 21:46
→ : 就是非同步為了確保程式正確按順序執行就要一個callbac2F 03/12 23:42
→ : k包著一個,非常的有事(咦
→ : 重點是很難一眼看懂,Promise只是把這件事變成一個鏈結
→ : ,但本質還是上面那樣
→ : k包著一個,非常的有事(咦
→ : 重點是很難一眼看懂,Promise只是把這件事變成一個鏈結
→ : ,但本質還是上面那樣
推 : 完全看懂了!!而且也會實際上的操作跟想法6F 03/13 01:33
→ : 真的非常非常感謝你用心回我 幫我建立觀念
→ : 其他語言寫習慣了 用js真的是又崩潰又有趣XD
→ : async/await 看起來真的精簡很多, 但支援度目前還不高?
→ : 真的非常非常感謝你用心回我 幫我建立觀念
→ : 其他語言寫習慣了 用js真的是又崩潰又有趣XD
→ : async/await 看起來真的精簡很多, 但支援度目前還不高?
推 : 推用心10F 03/13 02:32
→ : 對於支援度方面,可以參考 Babel/Buble 等的轉譯器11F 03/13 07:32
→ : https://babeljs.io/ https://buble.surge.sh/guide/
→ : https://babeljs.io/ https://buble.surge.sh/guide/
→ : 同步接異步不可能是因為js的event loop架構,在其他語言一13F 03/13 09:49
→ : 樣有這套async機制,但是真的要接回sync就把原thread block
→ : 即可,但在js中原則上不能也不應該這樣做
→ : 樣有這套async機制,但是真的要接回sync就把原thread block
→ : 即可,但在js中原則上不能也不應該這樣做
--
※ 看板: terievv 文章推薦值: 0 目前人氣: 0 累積人氣: 192
作者 eight0 的最新發文:
- 9F 4推
- 9F 5推
- 14F 7推
- timestamp 的值,是從 1970-01-01 00:00:00 (UTC) 開始計算, 每過 1ms 就增加 1。也可以說是從 1970-01-01 開始,到取得該 timestamp 的時間 …4F 4推
- setTimeout 接受兩種參數︰ var timeoutID = scope.setTimeout(function[, delay, param1, param2, ...]); var tim …11F 10推
點此顯示更多發文記錄
回列表(←)
分享