Geri arama beklerken Node.js'nin çıkması nasıl engellenir?


96

Bunun gibi bir kodum var:

var client = new mysql.Client(options);
console.log('Icanhasclient');

client.connect(function (err) {
  console.log('jannn');
  active_db = client;
  console.log(err);
  console.log('hest');

  if (callback) {
    if (err) {
      callback(err, null);
    }

    callback(null, active_db);
  }
});

Benim sorunum, Node'u çalıştırdığımda hemen sona eriyor. 'Icanhasclient' yazdırır, ancak geri aramadaki console.log dosyalarının hiçbiri çağrılmaz.

(Bu örnekteki mysql , node-mysql'dir .

Node.js'nin çıkmadan önce geri aramanın tamamlanmasını beklemesi için yapılabilecek bir şey var mı?


1
Çıkışı engellemek ne demek? Nodejs, tüm geri aramalar tamamlanana kadar çıkmaz. Bu tek iş parçacığı işlemidir.
neebz

4
@nEEbz: Eğer durum buysa, neden betiğim geri aramayı yürütmeden çıkıyor?
mikl

Veritabanınıza bağlanmakla ilgili bazı sorunlar var; Geri aramayı neden tetiklemediğinden pek emin değilim. Herhangi bir hata veriyor mu?
neebz

Hayır, sessizce başarısız oluyor :(
mikl

Bu çok tuhaf. Umarım buna bir cevap buluruz.
neebz

Yanıtlar:


50

Geri Arama Sıraya Alınmadı

Düğüm, tüm olay kuyrukları boşalana kadar çalışır . Bir geri arama, aşağıdaki gibi bir çağrı olduğunda olay kuyruğuna eklenir.

  emmiter1.on('this_event',callback).

idam etti. Bu çağrı, modül geliştiricisi tarafından yazılan kodun bir parçasıdır.

Bir modül, eşzamanlı / engelleyici bir sürümden hızlı bir bağlantı noktasıysa, işlemin bir kısmı tamamlanana kadar bu gerçekleşmeyebilir ve bundan önce tüm kuyruklar boşalabilir ve düğümün sessizce çıkmasına izin verebilir.

Bu, modül geliştiricisinin geliştirme sırasında karşılaşmayacağı sinsi bir hatadır, çünkü çok sayıda kuyruğa sahip meşgul sistemlerde , kritik zamanda hepsinin boş kalması nadir olacaktır.

Kullanıcı için olası bir düzeltme / hata algılayıcı, şüpheli işlev çağrısından önce özel bir zamanlayıcı olayı eklemektir.


"bu işlemin bir kısmı tamamlanana kadar gerçekleşmeyebilir" - Bunu açıklayabilir misiniz? Düğümün, olay kuyruğuna geri arama ekleme şansı bulamadan, bir kod parçası hala çalışırken çıkabileceğini mi söylüyorsunuz?
BT

Sanırım, eğer kütüphane bir zamanlar senkronize olan kodu yeni bir asenkron sürüme dönüştürdüyse, kütüphane oluşturucuların geri aramaları sıraya koymayı unutmuş olmaları ve olay kuyruklarının yürütmenizin ortasında bir yerde boşaltılması ve böylece çağrılmadan bitirilmesi anlamına gelir. geri aramalar.
Dielson Satışları

1
Bu sorunu güvenilir bir şekilde yeniden oluşturmanın kolay bir yolu var mı?
Jordan Brough

Bunun için teşekkürler! Bir "asılı" sürecim vardı ve neden bırakmayacağını anlamadım. Tüm dinleyicilerimi / olay geri aramalarımı temizledikten sonra işlemden çıkıldı.
ps2goat

7
UYARI - Çok sayıda olumlu oy almamıza rağmen, bu yanıttaki bilgilerin yanlış olduğunu düşünüyorum. Arayan EventEmitter.on()yok değil Düğüm (Node geçerli sürümünde en azından) için bekleyeceği olay döngü üzerine bir şey eklemek. Yalnızca zaman aşımlarını ayarlama ve çekirdek kitaplıktan eşzamansız çağrılar yapma gibi şeyler bunu yapabilir. Asla ateşlenmeyecek bir olay bekleyen tek satırlık bir program yazarak bunu kolayca test edebilirsiniz. Bu cevap itibaren, bunu sonlandırmak asla beklediğiniz, ama yok anında sona erer. Daha fazla bilgi için bu yanıta bakın .
davnicwil

40

SetInterval ile sadece bir setTimeout veya yinelenen bir zaman aşımı oluşturabilirsiniz.

Çıkış koşullarını kontrol etmek istiyorsanız, koşullu bir zaman aşımı da yapabilirsiniz:

(function wait () {
   if (!SOME_EXIT_CONDITION) setTimeout(wait, 1000);
})();

Bunu kodunuzun sonuna koyun ve konsol siz kapanıncaya kadar bekleyecek ... ve bekleyecektir.


5
Bu iyi bir çözümdür, ancak hoş bir çözüm değildir (sizin hatanız değil, düğümler).
peterh - Monica'yı eski durumuna getir

14

Çözümüm bir EventEmiter'ı somutlaştırmak ve özel olayımı dinlemekti.

var eventEmitter = new process.EventEmitter();

sonra eventEmitter.emiteşzamansız geri aramadan aradım :

client.connect(function (err) {
    eventEmitter.emit('myevent', {something: "Bla"})
});

Senaryomdaki son şey şuydu eventEmitter.on:

eventEmitter.on('myevent', function(myResult){
  // I needed the result to be written to stdout so that the calling process could get it
  process.stdout.write(JSON.stringify(myResult));
});

Düğüm daha sonra olay işleyicisi çalışmayı bitirene kadar bekleyecektir.


1
"Process.EventEmitter bir yapıcı değil" hatası alıyorum. Ne öneriyorsun?
Anthony Kong

EventEmiter'ın nasıl oluşturulacağına bakın. coligo.io/nodejs-event-emitter iyi bir kaynak gibi görünüyor. Node'un farklı sürümleri bir tane yaratma şeklini değiştirmiş olabilir.
you786

2
EventEmittersınıf eventsmodül tarafından tanımlanır . Yani, yeni bir yayıcı oluşturmak için: const EventEmitter = require('events'); var eventEmitter = new EventEmitter().
CedX

1
UYARI - kabul edilen cevap hakkındaki yorumuma bakın. Bunun yanlış olduğunu düşünüyorum ve en azından düğümün mevcut sürümü için çalışmıyor.
davnicwil

5

@ Todd'un cevabına dayanarak, tek satırlık bir çizgi oluşturdum. Bunu komut dosyanızın başına ekleyin ve done = trueişiniz bittiğinde ayarlayın:

var done = (function wait () { if (!done) setTimeout(wait, 1000) })();

Misal:

var done = (function wait () { if (!done) setTimeout(wait, 1000) })();

someAsyncOperation().then(() => {
  console.log('Good to go!');
  done = true;
});

O nasıl çalışır? Biraz genişletirsek:

// Initialize the variable `done` to `undefined`
// Create the function wait, which is available inside itself
// Note: `var` is hoisted but `let` is not so we need to use `var`
var done = (function wait () {

  // As long as it's nor marked as done, create a new event+queue
  if (!done) setTimeout(wait, 1000);

  // No return value; done will resolve to false (undefined)
})();

1
IIFE'nin çıktısını neden bitirdiniz, basitçe yanlış, bundan sonra IIFE ile daha iyi olur.
tiffon

1
@tiffon Muhtemelen sadece tek astar yapmak için.
Pedro A

0

İşte benim iki sentim:

async function main()
{
    await new Promise(function () {});
    console.log('This text will never be printed');
}

function panic(error)
{
    console.error(error);
    process.exit(1);
}

// https://stackoverflow.com/a/46916601/1478566
main().catch(panic).finally(clearInterval.bind(null, setInterval(a=>a, 1E9)));

0

İşte bunu nasıl yapıyorum. Ana giriş noktamın bir söz vermesini sağlıyorum, ardından bu küçük sarmalayıcıyı söz konusu söz yerine getirilmediği sürece düğümün çıkmayacağından emin olmak için kullanıyorum:

function wrapPromiseMain(entryPoint) {
    const pollTime = 1000000;
    let interval = setInterval(() => {}, pollTime);
    
    return entryPoint().finally(() => {clearInterval(interval)});
}

Kullanmak için, ana giriş noktanızı vaat edin ve bunu sarmalayıcıya bir argüman olarak iletin:

function main() {

    // ... main stuff ...

    return doSomethingAsync();
}

wrapPromiseMain(main);

Bunu temel yoklama döngülerinden biraz daha düzenli buluyorum çünkü vaat gerçekleştiğinde zamanlayıcıyı otomatik olarak iptal edecek, böylece herhangi bir ek gecikme eklemeyecek. Bu nedenle, yoklama süresi, isterseniz temelde sonsuza kadar olabilir.


Not: gönderdikten sonra düzenlendi çünkü setTimeout'a zincirlenmiş çağrılar yerine setInterval kullanarak daha da özlü hale getirebileceğimi fark ettim.
ad1c371f7d05

-1

Ben bakmak yaptım felixge / düğüm-mysql kütüphane ve API komut client.connect bir başvuru görmedim. Yapmaya çalıştığınız asıl arama bu mu (burada niteleyici olmaya çalışmıyorum)? Ne olursa olsun, IMHO Javascript'in nasıl tasarlandığı hakkında daha fazla düşünmeniz gerekiyor çünkü diğer popüler dillerin çoğundan farklı bir programlama paradigması kullanıyor.

Kodunuzda gördüğüm ilk sorun, geri aramayı tanımlamamış olmanız, dolayısıyla aslında mevcut olmamasıdır. Console.log'un (callback) tanımsız olduğunu varsayıyorum. Kodunuzdan, anonim işlev, client.connect işlevi için 'geri arama'dır. 'Geri arama' dediğiniz şeyi daha yüksek bir kapsamda tanımlamanız gerekir. Örneğin, client.connect'in anonim işlevinden daha yüksek kapsamda var olması için myCallback işlevini tanımlayacağım. Javacscript değişken kapsamına bakmak faydalı olabilir .

    var myCallback(err, response) {
      if (err) {
        console.log('err:%s',err);
      } else {
        console.log('response:%s',response);
      }
    }

    client.connect(err, function(response) {
      // this anonymous function is the callback to client.connect, the var
      // 'callback' would be undefined.
      if (err) {
        myCallback(err);
        return; // Explicit call to return, else the lines below would run.
      } 
      myCallback(null, response);
    });

İkinci olarak, Javascript içinde açıkça return çağırmazsanız, işlev işlemeye devam edecektir. Bunun tarafından ısırıldı kendim . Son olarak, Javascript olay odaklı bir döngü çalıştırır, yani işlevlerin bir değer döndürmesini asla beklemeyecektir, bu nedenle ilk etapta tüm bu geri aramalara sahibiz. Örneğin bir koşul doğru olana kadar while döngüsü kullanarak Javascript'i farklı davranmaya zorlayabilirsiniz. Olay döngüsünü değiştirmenin çeşitli stratejileri için caolan tarafından hazırlanan 'eşzamansız' kitaplığa bakın . Bu yöntemleri aşırı kullanmanın en büyük dezavantajı, muhtemelen daha fazla geri çağırma kullanmanız ve programlarınızın nasıl çalıştığını yeniden düşünmeniz gerektiğinde, aslında CPU döngülerini / engellemeyi boşa harcamanızdır.


-2

Lütfen bunu deneyin. Bunun yardımcı olup olmadığını kontrol edin.

var client = new mysql.Client(options);
console.log('Icanhasclient');
var verbose;

if (!verbose) {
    return new Promise(function (resolve, reject) {
        client.connect(function (err) {
            if (err) {
                console.log(Error in connecting
                SQL ${err}
            )
                ;
                return reject(err);
            }
            verbose = client;
            return resolve(verbose);
        })
    })
} else {
    return new Promise(function (resolve) {
        resolve(verbose);
    })
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.