簡體   English   中英

如何訪問Promise.then中的范圍變量(類似於closure)

[英]How to access out of scope variables in Promise.then (similar to closure)

難以理解,確實有一種優雅的方式來做到這一點,但不知道是什么。

我想要像:

let x = 5;

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve();
  }, 2000);
}).then(() => {
  console.log(x);
});

x = 3;
// Print out 5 after 2 seconds.

基本上,給定類似於上面的設置,有沒有辦法打印'5' ,無論在異步超時期間x的值是否改變? 在我的情況下,很難簡單地在resolve()傳遞x

你可以通過IIFE傳遞它:

 let x = 5; const p = (x => new Promise((resolve, reject) => { // ^ use it here setTimeout(() => { resolve(); }, 2000); }).then(() => { console.log(x); }))(x); // ^ pass it here x = 3; 

這樣做的原因是因為我們正在通過我們的函數創建一個范圍,該函數將變量x作為其參數之一綁定到傳遞給IIFE的任何值。

這允許我們將全局x綁定到其他東西,但IIFE內的x不受影響。

由於我們在IIFE內部和外部使用相同的名稱,因此內部x也遮蔽了外部的名稱。

也許使用不同的名稱會使事情更具可讀性:

 let x = 5; const p = (y => new Promise((resolve, reject) => { // ^ use it here under a different name setTimeout(() => { resolve(); }, 2000); }).then(() => { console.log(y); }))(x); // ^ pass it here x = 3; 


注意:上面的工作原理是因為我們處理的是原始值, 它們在JavaScript中是不可變的 ,因此在每次重新賦值時都會重新創建一個新值。

 var a = 'a'; var b = a; // this will bind `b` to the copy of value of `a` a = 'changed'; // this won't affect `b` console.log(a, b); // 'changed', 'a' 

如果我們處理對象,使用IIFE將無法工作:

 let x = { changed: false }; const p = (y => new Promise((resolve, reject) => { // ^ still points to the same object as x setTimeout(() => { resolve(); }, 2000); }).then(() => { console.log(y); }))(x); x.changed = true; // this will affect y as well 

原因是對象不是不可變的,因此每個綁定變量都指向同一個對象。

 var a = { name: 'a' }; var b = a; // this will bind `b` to the value of `a` (not copy) a.name = 'changed'; // this will also change `b` console.log(a.name, b.name); // 'changed', 'changed' 

為了實現對象所需的內容,您必須模仿JS引擎對基元的作用,並在將對象傳遞給IIFE時克隆該對象:

 let x = { changed: false }; const p = (y => new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, 2000); }).then(() => { console.log(y); }))({ ...x }); // ^^^^^^^^ clone x when passing in x.changed = true; // now this only affects the original, not the clone 

或者使用Object.assign

 let x = { changed: false }; const p = (y => new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, 2000); }).then(() => { console.log(y); }))(Object.assign({}, x)); // ^^^^^^^^^^^^^^^^^^^ clone x when passing in x.changed = true; // now this only affects the original, not the clone 

注意:對象傳播Object.assign執行淺層克隆。 對於深度克隆,您可以在NPM上找到許多庫

請參閱: 在JavaScript中深度克隆對象的最有效方法是什么?

對於大多數情況,這也可以工作:

 let x = { changed: false }; const p = (y => new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, 2000); }).then(() => { console.log(y); }))(JSON.parse(JSON.stringify(x))); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ clone x when passing in x.changed = true; // now this only affects the original, not the clone 


注意:使用IIFE只是一個簡單的例子。 常規函數也可以正常工作(但對於非原始值仍然有相同的問題):

 let x = 5; const p = createPromise(x); x = 3; function createPromise(y) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(); }, 2000); }).then(() => { console.log(y); }) } 

是的,您可以使用工廠函數生成您的承諾,該承諾可以充當變量的閉包。

function promiseFactory(x){
    return new Promise(function(resolve){
        setTimeout(function(){
            console.log(x); // value as passed to factory call
             resolve(x)
        }, 1000)
    });
}

let x = 5;
promiseFactory(x) // returns a promise which will always see x as 5
    .then(function(x){console.log(x)})

一個小警告:這可以在這里工作,因為x是一個整數,它是一個基本類型,因此值被復制。 如果您使用像object / array這樣的引用類型,則必須傳遞克隆對象

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM