chrome.storage.local fiyaskosu
merhaba,
bir borsa eklentisi yazıyorum. normalde sunucuda yapacaktım, ama kişi tarayıcı eklentisini tercih etti. sinirden beynimden vurulmuşa döndüm.
standart localStorage kullanımını bilmeyen yoktur herhalde
let lSet = (name, value) => localStorage.setItem(name, value),
lGet = name => localStorage.getItem(name);
ne güzel sade kısa öz, kullanılabilir. üstelik boolean türünde veride döndürüyor sorguladığınızda
var data = localStorage.getItem('data') ?
localStorage.getItem('data') :
localStorage.setItem('data', 'data verisi');
sorguladım varsa seç, yoksa oluştur öyle seç dedim. pratik. ister döngüye al kullan, ister bir tane kullan
data.obj.forEach(([key => val]) =>{
localStorage(key, val);
});
bir anda yüzlerce bile oluşturabildim. peki google napmış? bu güzelim yöntemi eklentiler için devre dışı bırakmış,
let data = {},
lSet = (name, value) => {
chrome.storage.local.set({[name]: value}, function() {
data[name] = value;
});
},
lGet = name => {
chrome.storage.local.get([name], function(result) {
return result.key;
});
},
hadi al sok bunu döngüye. değerin ne olduğu, nereye gittiği belli değil. bu kadar yorucu bi yöntem olur mu ya, hammallık resmen bu. arkadaş ben niye yöntem yazıp, içerisinde callback olarak tanımlama yapmak zorundayım. standart kullanımın yalın haliyle bile, ayarların konfigürasyonu ve verilerin çerez olarak depolanması için 479 satırlık sınıf yazdım. bu şekilde yaparsam, çoğu veriyi döngüye alamam uygun değil. çoklu dil desteğinden, çok sayıda form elemanlarının ayarına kadar veriyi localStorage de tuttum. tutmak zorundayım çünkü statik bir veri yok ortada. ne yapayım şimdi ben, iptal mi edeyim siparişi? hayır en çok zoruma giden? niye yani niye niye neşter dururken döner bıçağıyla ameliyata yolluyor beni google. set ettiğim değeride geri alamıyorum bu arada, anca get içerisinde calback yapacağım orda da kullanmak istediğim veriyle değişkeni eşitleyeceğim.
lütfen birileri benim görmediğim olumlu bir tarafını görüyorsa bu durumun, bana da göstersin.
Soru hatalı mı? 👎
Eğer sorunun kurallara aykırı olduğunu düşünüyorsanız lütfen bize bildirin!
Cevaplar (8)
pes ettim. az kod çok iş mantığıyla kod yazmak istemiştim olmadı, ilk çözüme geri dönüyorum. her yöntem için async sarmalayıcı yazıp beklettikten sonra, yazıyorum ne yazcaksam.
Promise.all([this.getData()]).then(obj => {});
o kadar kod ekledikten sonra, şunu eklememek için çektiğim ızdraba rağmen, ekliyorum. konuya kimsenin fikir belirtmemesi de ayrı ayıp oldu.
Herkese çok detaylı, uzun ve teknik cevaplar vermeye çalışmamın nedeni aslında cevabı doğrudan soruyu sorana yönelik yazmamam.
Aslında çoğu kez soruyu soranların zaten cevaplarımda anlattıklarımın çoğunu bildiklerini farkındayım.
Ama ben cevaplarımı, konu hakkında az bilgisi olup da araştırırken cevabımı gören olursa onun için de anlaşılır olsun diye düşünerek yazıyorum.
Ama tabi prototurk.com soru cevap bölümünün arama özelliği henüz iyileştirilmediği için çoğunlukla yazdıklarım boşa gidiyor tabi... :( Ama bir gün arama bölümü geliştirilirse kaynaklar hazır olsun istiyorum.
Tayfun Erbilen'e instagram üzerinden buraların geliştirilmesine destek olabileceğimi söyledim ama ya görmedi ya güvenmedi. :)
Açıkçası Chrome eklentisi yazmak üzerine "Merhaba Dünya" dışında pek pratiğim yok.
Extension yazarken storage'a bu şekilde veri basıldığını bile sorunuzdan öğrendim. :)
Cevabım çalışsaymış iyiymiş. :/
bazen sana şaşırıyorum, herkese çok detaylı uzun ve teknik cevaplar veriyorsun. iki mesajı da okudum, bahsettiğin örneği de uyguladım, yeni aynı sonucu aldım, aslında 3. mesajımda yer alan callback'den bir farkı yok, sadece ben daha kısa yazmışım. birde siz, now anahtarını da bu callback'a almışsınız. çok fazla şey denedim aslında, dediğiniz gibi işlem bitmeden değerleri okumaya çalıştığımı fark etmemin üzerindne çok geçti. ama ne yaparsam yapayım, asekron döngü yazıp değerin dönmesini bekletsem bile değer alamıyorum. sizin verdiğiniz örnekte başarısızlıkla sonuçlandı. başta şöyle düşündüm, kurucu içerisinde tek özelliğe tüm ayarları aktarırsam, diğer yöntemler içerisinde ekstra kod yazmadan işimi görürüm. bu yüzden bu kadar zorladım, aman bu da böyle olsun demedim. nitekim olmadı : )
kendi kodunuzu test etmek istersenizde
//manifest.json
{
"name": "Olymp Trade Transactions",
"description": "For technical support, you can always contact me on fiver. https://www.fiverr.com/****",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "index.html",
"default_icon": "logo.png"
},
"permissions": [
"storage",
"unlimitedStorage",
"notifications",
"contextMenus"
]
}
sıradan bir klasöre bunu ve index.html olarak kodunuzu ekleyebilirsiniz.
Ben sorunuzu görüp diğer cevaplarınızı da okuyup anlayıp cevap yazmaya başladığımda pes etmişsiniz :D
Doğru cevabınızı görmeden bir cevap yazmış oldum (aşağılarda bulabilirsiniz).
Benim cevabımı yazmaya başlamam asenkron bir fonksiyon olsun ve bir obje döndürecek olsun.
Sizin doğru cevap olarak işaretlediğiniz cevap da asenkron bir fonksiyon olsun ve o da obje döndürecek olsun.
Benim fonksiyonum sizinkinden önce başladı ama sonucu sizinkinden sonra döndü.
Eğer sizin cevabınızın sonunda console.log(ebykdrms.cevap, munzevi.cevap)
diye bir komut olsaydı
normalde beklenen şey benim cevabım henüz oluşmadığı için (çünkü sizin fonksiyonunuzdayız) ilk parametre için undefined, ikinci parametre için sizin cevabınız olurdu.
Gel gelelim javascript (cevaplar obje oldukları için) ikimizin de cevabını ekrana basacaktı.
Ama şöyle doğrudan string'ler içeren bir komut verseydiniz:
console.log(ebykdrms.cevap.metin, munzevi.cevap.metin);
o zaman ilk parametre undefined olarak görünecekti.
Bu da öylesine bir cevap olsun :D
Adım adım işlemleri okuduğumda şöyle bir işleyiş gördüm:
1) Sınıftan nesne oluşturduğunuz anda kurucu fonksiyon çalışıyor.
2) conf = {}
ataması yapıyorsunuz.
3) storageData() fonksiyonunu çağırarak data = { lang: 'en', testData1: 1, testdata2: 2 }
işlemini sağlyorsunuz.
4) data
daki veriyi Object.entries()
işlevinden geçirip şu diziyi elde ediyorsunuz:
[ ["lang", "en"], ["testData1", 1], ["testData2", 2] ]
5) forEach()
ile bu dizi üzerinde dönmeye başlıyorsunuz. Döngüyü adım adım işlersek:
5.1) key="lang"
ve val="en"
iken chrome.storage.local.get("lang", (obj) => {...})
işlevini çağırıyorsunuz. Bu asenkron bir fonksiyon olduğu için henüz sonuçlanmadan döngünün 2.adımına geçiyorsunuz.
5.2) key="testData1"
ve val=1
iken chrome.storage.local.get("testData1", (obj) => {...})
işlevini çağırıyorsunuz. Bu asenkron bir fonksiyon olduğu için henüz sonuçlanmadan döngünün 3.adımına geçiyorsunuz.
5.3) key="testData2"
ve val=2
iken chrome.storage.local.get("testData2", (obj) => {...})
işlevini çağırıyorsunuz. Bu asenkron bir fonksiyon olduğu için henüz sonuçlanmadan döngünüz bitiyor.
6) Döngü bitti. Henüz asenkron fonksiyonlar sonuç döndürmedi. Yani getData2()
fonksiyonu çalışmadığı için henüz conf.lang
değeri de dolmadı.
7) conf.now
key'ini doldurduğunuz sırada henüz conf.lang
key'i tanımlanmadı.
console.log(this.conf, this.conf.lang)
sizi yanıltıyor. İlk parametrede obje, ikinci parametrede string gönderdiğiniz için, javascript de objelere ve string'lere farklı yaklaşımlar sergilediği için size yanıltıcı sonuçlar gösteriyor.
console.log()
fonksiyonu okunduğu andan ekrana basma işlemini gerçekleştirene kadar geçen sürede this.conf.lang
değeri dolmuş oluyor.
Yani console.log(this.conf, this.conf.lang)
komutunu verdiğiniz anda şunu söylemiş oluyorsunuz: console.log(obje, "string")
Ekrana basılma sırasında console.log()
string değeri doğrudan hafızasında tutarken, ilk parametredeki objenin sadece referansını tutuyor. Bu yüzden ekrana basılırkenki string değer gerçekten console.log()
komutu ilk çağırıldığındaki değer olurken obje değeri ekrana basılma anındaki değer neyse o oluyor.
Eğer objenin de o andaki değerini almak istiyorsanız komutu şu şekilde düzeltmelisiniz:
console.log(JSON.stringify(this.conf), this.conf.lang);
Böylece iki parametrede de string değer aldığı için çağırıldığı andaki değerler neyse onu yazdıracaktır.
Yani sizin obje olarak basınca değer var ama direkt değeri basınca değer yok olarak bahsettiğiniz olay javascript'in objeleri tutuş biçimindeki yaklaşımından kaynaklanıyor. (Ki aslında bu tutuş biçimi javascript'in yüksek performanslı olmasını sağlayan en büyük etkenlerden biri)
Sorunun çözümü olarak, emin değilim ama, conf.now
key'ine değer atama işlemini callback fonksiyonunun içinde yapmanız gerekiyor olabilir.
Yani:
constructor() {
this.conf = {};
Object.entries(this.storageData().data).forEach(([key, val]) => {
chrome.storage.local.get(key, obj => {
if(!obj[key]) chrome.storage.local.set({[key]:val}, this.getData2({[key]:val}));
else this.getData2(obj);
if(key=="lang") {
this.conf.now = new Intl.DateTimeFormat(this.conf.lang, {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'longGeneric',
timeZone: this.conf.timeZone
}).format(new Date(Date.now()));
console.log("in callback: ", JSON.parse(JSON.strinify(this.conf)), this.conf.lang);
}
});
});
console.log("in constructor: ", JSON.parse(JSON.strinify(this.conf)), this.conf.lang);
}
iki adet örnek bırakıyorum konuyu aydınlatabilecek bir bilgiye ciddi anlamda ihtiyacım var. sorun ise, tanımlı nesnelerin anahtar belirtildiğinde tanımsız dönmesi.
bu callback kullandığım örneğim;
class Conf {
constructor() {
this.conf = {};
Object.entries(this.storageData().data).forEach(([key, val]) => {
chrome.storage.local.get(key, obj => {
!obj[key] ?
chrome.storage.local.set(
{[key]:val}, this.getData2({[key]:val})) :
this.getData2(obj);
});
});
this.conf.now = new Intl
.DateTimeFormat(this.conf.lang, {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'longGeneric',
timeZone: this.conf.timeZone
}).format(new Date(Date.now()));
console.log(this.conf, this.conf.lang) //output parameters 1: succes, parameter: 2 failed
}
getData2(data){
Object.assign(this.conf, data)
console.log(this.conf.lang) // output: succes
}
storageData(){
this.data = {
lang : 'en',
testData1: 1,
testdata2: 2,
}
return this;
}
}
(function(){ new Conf; })();
bu da promise kullandığım örneğim;
class Conf {
getData2(data){
Object.assign(this.conf, data)
}
async getData(){
const obj = new Promise(resolve =>{
resolve(this.storageLocal());
}).then(obj => { return obj });
this.conf = await obj;
console.log(this.conf, this.conf.lang);
}
storageLocal(){
this.conf = {};
/*
checks the specified cookie, if there is no cookie it automatically creates the specified cookie
&&
selects the created cookie as object and returns value
@String key => key value of the object contained in the cookie
@Object val => configuration object contained in the cookie
*/
Object.entries(this.storageData().data).forEach(([key, val]) => {
chrome.storage.local.get(key, obj => {
!obj[key] ?
chrome.storage.local.set(
{[key]:val}, this.getData2({[key]:val})) :
this.getData2(obj);
});
});
this.conf.now = new Intl
.DateTimeFormat(this.conf.lang, {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'longGeneric',
timeZone: this.conf.timeZone
}).format(new Date(Date.now()));
return this.conf;
}
storageData(){
this.data = {
lang : 'en',
testData1: 1,
testdata2: 2,
}
return this;
}
}
(function(){
var Settings = new Conf;
Settings.getData();
})();
artık inceden https://www.youtube.com/watch?v=hmCiKvzNkkw şu kıza bağladım. dedim ki belki veri yüklenmeden koşullar çalışıyordur, promise kullanayım
const obj = new Promise(resolve =>{
resolve(this.storageLocal());
}).then(obj => { return obj });
this.conf = await obj;
console.log(
this.conf,
this.conf.lang,
this.conf['lang']
)
yine olmuyor. tüm objeyi basıyor, anahtar belirtince basmıyor.
ilk şoku atlatıp, sinir krizim geçince oturup yazmaya devam ettim : )
ama mantıksız olan bir noktada takıldım.
burada diyorum ki, chrome.storage.local.get
yönteminde belirtilen anahtar yoksa yenisini oluştur, daha sonrasında callback ile boş nesneye dönen sonucu aktarıyorum;
let conf = {};
Object.entries(this.storageData().data).forEach(([key, val]) => {
chrome.storage.local.get(key, object => {
if(!object[key])
chrome.storage.local.set(
{[key]:val},
function(){
Object.assign(conf, {[key]:val})
}
);
});
});
yukarıdaki kod, ilk girişte çalıştırılacak bir kod, daha sonraki oturumlarda ihtiyaç duyulmayacak. dolayısıyla bu seferde mevcut depolanan verileri okumam gerkekiyor;
chrome.storage.local.get(null, items => {
Object.assign(conf, items);
});
onu da bununla hallediyorum.
<a href="https://imgur.com/LZjSic1.png" target="_blank">şimdi sorun tam şu noktada ortaya çıkıyor;</a>
console.log(
this.conf,
this.conf.lang,
conf,
conf.lang
);
this.conf
ve conf
tuttuğu tüm objeleri consola basıyor, ama this.conf.lang
ve conf.lang
undefined
dönüyor.
bitti mi bitmedi,
new Intl
.DateTimeFormat(conf.lang, {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'longGeneric',
timeZone: conf.timeZone
}).format(new Date(Date.now()));
aynı bloğun içinde olan bu kodda conf.lang
undefined
dönmüyor.
<span style="color:red;font-size:16px;font-family:sans">bu durumun sebebi ne olabilir?</span>
tam kod örneği;
let conf = {};
Object.entries(this.storageData().data).forEach(([key, val]) => {
chrome.storage.local.get(key, object => {
if(!object[key])
chrome.storage.local.set(
{[key]:val},
function(){
Object.assign(conf, {[key]:val})
}
);
});
});
chrome.storage.local.get(null, items => {
Object.assign(conf, items);
});
//conf["data"] = conf.langs[conf.lang];
conf["now"] = new Intl
.DateTimeFormat(conf.lang, {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'longGeneric',
timeZone: conf.timeZone
}).format(new Date(Date.now()));
this.conf = new Object(conf);
console.log(
this.conf,
this.conf.lang,
conf,
conf.lang
);