Node.js自學筆記 (4/12)

提醒

image
若node.js的執行過程中,發生上圖的錯誤,就會造成整個網站後台crash,後面的程式就不會繼續執行了。為了避免這種情況,可以使用 try...catch... 語法,捕捉錯誤的程式碼。

舉例來說:

        function func(){
            return new Promise((resolve, reject)=>{
                setTimeout(function(){
                    resolve(Math.random())
                }, 500)
            })
        }

        func()
        .then(r=>{
            info.innerHTML += `<p>${r}</p>`;
            return func();
        })
        .then(r=>{
            info.innerHTML += `<p>${r}</p>`;
        })

        console.log('ooo')
        const str = '{"a":1,"b":"ben}'
        let obj = JSON.parse(str)
        console.log('kkk')

  1. promise會執行
  2. console.log('ooo') 會執行
  3. JSON.parse(str) 出現錯誤
  4. console.log('kkk') 便被中斷,不會執行了
  5. 合理推測javascript底層是多個執行序在執行

如果是這樣寫的話,就連promise也不會執行到,因為被中間的錯誤javascript所截止中斷了。

        console.log('ooo')
        const str = '{"a":1,"b":"ben}'
        let obj = JSON.parse(str)
        console.log('kkk')


        function func(){
            return new Promise((resolve, reject)=>{
                setTimeout(function(){
                    resolve(Math.random())
                }, 500)
            })
        }

        func()
        .then(r=>{
            info.innerHTML += `<p>${r}</p>`;
            return func();
        })
        .then(r=>{
            info.innerHTML += `<p>${r}</p>`;
        })

使用 try... catch... 的寫法:

        console.log('ooo')
        const str = '{"a":1,"b":"ben}'
        let obj = {};
        try {
            obj = JSON.parse(str)
            console.log('-------')
        } catch (ex) {
            console.log('ex:',ex)
        } finally {
            console.log('try catch finally')
        }
        
        console.log('kkk')
        console.log(obj)

JSON.stringify

<body>
    <div id="info"></div>
    <script>
        const p1 = {
            name: 'Ben',
            age: 18
        }
        const p2 = {
            name: 'David',
            age: 25
        }
        p1.next = p2
        info.innerHTML = JSON.stringify(p1);

    </script>
</body>

執行結果: {"name":"Ben","age":18,"next":{"name":"David","age":25}}
其中,next變成p1的一個屬性。

若程式改為如下:

<body>
    <div id="info"></div>
    <script>
        const p1 = {
            name: 'Ben',
            age: 18
        }
        const p2 = {
            name: 'David',
            age: 25
        }
        const p3 = {
            name: 'Peter',
            age: 26
        }
        p1.next = p2
        p2.next = p3
        info.innerHTML = JSON.stringify(p1);

    </script>
</body>

執行結果為: {"name":"Ben","age":18,"next":{"name":"David","age":25,"next":{"name":"Peter","age":26}}}

現在問題是,若再加上這一段程式: p3.next = p1 ,這樣就變成繞圈圈了。

如果先把 info.innerHTML = JSON.stringify(p1); 拿掉,則程式為:

<body>
    <div id="info"></div>
    <script>
        const p1 = {
            name: 'Ben',
            age: 18
        }
        const p2 = {
            name: 'David',
            age: 25
        }
        const p3 = {
            name: 'Peter',
            age: 26
        }
        p1.next = p2
        p2.next = p3
        p3.next = p1
        // info.innerHTML = JSON.stringify(p1);

    </script>
</body>

在瀏覽器的執行結果為:


結果為沒有錯誤,而且使用console去取值都是正常。
image
其結構為圈圈狀的結構。

這樣圈圈裝的結構如果要使用語法 info.innerHTML = JSON.stringify(p1); 將之轉換為字串,就會出錯。
image

另一個promise的例子

<body>
    <div id="info">
        <p>123</p>
    </div>
    <script>
        function func(msec){
            return new Promise((resolve, reject)=>{
                setTimeout(function(){
                    resolve(Math.random()*msec)
                }, msec)
            })
        }

        const ar=[];
        func(800)
        .then(r=>{
            ar.push(r);
            return func(700);
        })
        .then(r=>{
            ar.push(r);
            return func(800);
        })
        .then(r=>{
            ar.push(r);
            info.innerHTML += JSON.stringify(ar);
        }

        ) 
    </script>
</body>

執行結果:

其實以上還是callback function,只是傳統的callback function會凹進去,現在只是拉平,只是在程式碼維護管理會比較方便。

現在要在進一步作簡化,嘗試把以下程式碼包裝成一個function:

        const ar=[];
        func(800)
        .then(r=>{
            ar.push(r);
            return func(700);
        })
        .then(r=>{
            ar.push(r);
            return func(800);
        })
        .then(r=>{
            ar.push(r);
            info.innerHTML += JSON.stringify(ar);
        }

所以程式碼如下:

<body>
    <div id="info">
        <p>123</p>
    </div>
    <script>
        function func(msec) {
            return new Promise((resolve, reject) => {
                setTimeout(function () {
                    resolve(Math.random() * msec)
                }, msec)
            })
        }

        function doSomething() {
            const ar = [];
            func(800)
                .then(r => {
                    ar.push(r);
                    return func(700);
                })
                .then(r => {
                    ar.push(r);
                    return func(800);
                })
                .then(r => {
                    ar.push(r);
                    // info.innerHTML += JSON.stringify(ar);
                    return ar;
                })
        }
        
        const br = doSomething();
    </script>
</body>

以上程式碼, br 無法取得 returnar ,因為

func(800)
        .then(r => { .....

以下的程式是非同步的, function doSomething() {... 是同步的,一下子就跑完了,所以不會等 arreturn ,所以 br 會取到空值。

若在程式插入一些識別的記號,程式改寫如下:

<body>
    <div id="info">
        <p>123</p>
    </div>
    <script>
        function func(msec) {
            return new Promise((resolve, reject) => {
                setTimeout(function () {
                    resolve(Math.random() * msec)
                }, msec)
            })
        }

        function doSomething() {
            console.log(1)
            const ar = [];
            func(800)
                .then(r => {
                    console.log(2)
                    ar.push(r);
                    return func(700);
                })
                .then(r => {
                    ar.push(r);
                    return func(800);
                })
                .then(r => {
                    ar.push(r);
                    // info.innerHTML += JSON.stringify(ar);
                    return ar;
                })
                console.log(3)
        }
        
        const br = doSomething();
        console.log('br:',br)
    </script>
</body>

那麼程式執行的結果為:

所以br不會取得結果。

這個問題要用 async await 來解決,這是屬於ES7的語法。

所以上面的程式改寫如下:

<body>
    <div id="info">
        <p>123</p>
    </div>
    <script>
        function func(msec) {
            return new Promise((resolve, reject) => {
                setTimeout(function () {
                    resolve(Math.random() * msec)
                }, msec)
            })
        }

        async function doSomething() {
            console.log(1)
            const ar = [];
            ar.push(await func(800));
            console.log(2)
            ar.push(await func(700));
            ar.push(await func(600));
            console.log(3)
            return ar;
        }

        const br = doSomething();
        console.log('br:', br)
    </script>
</body>

這樣的寫法, br 同樣無法取得資料。

先說明一下:

  1. 使用 async 宣告 function,裡面 await 後面接的必須是 promise物件 ,這是屬於非同步的操作
  2. await 必須在一個由 async 宣告的 function 裡面 (這是規定)
  3. async function看起來是同步的,但其底層是非同步的。
  4. async 包起來的 function ,也會變成 promise
  5. async await 是建立在 promise 的基礎之上,如果沒有 promise 的語法程式,就不能使用 async await

OK,既然 async function doSomething(){} 變成promise,那麼程式可改寫如下:

<body>
    <div id="info"></div>
    <script>
        function func(msec) {
            return new Promise((resolve, reject) => {
                setTimeout(function () {
                    resolve(Math.random() * msec)
                }, msec)
            })
        }

        async function doSomething() {
            console.log(1)
            const ar = [];
            ar.push(await func(800));
            console.log(2)
            ar.push(await func(700));
            ar.push(await func(600));
            console.log(3)
            return ar;
            // info.innerHTML += JSON.stringify(ar);
        }

        doSomething()
        .then(a=>{
            info.innerHTML += JSON.stringify(a);
        })
    </script>
</body>

執行結果為:

使用匿名函式來寫:

<body>
    <div id="info"></div>
    <script>
        function func(msec) {
            return new Promise((resolve, reject) => {
                setTimeout(function () {
                    resolve(Math.random() * msec)
                }, msec)
            })
        }

        async function doSomething() {
            console.log(1)
            const ar = [];
            ar.push(await func(800));
            console.log(2)
            ar.push(await func(700));
            ar.push(await func(600));
            console.log(3)
            return ar;
            // info.innerHTML += JSON.stringify(ar);
        }

        let br = [];

        (async ()=>{
            br = await doSomething();
            console.log('br:',br);
        })();
    </script>
</body>

執行結果為:
image