# queue.mjs

    import evem from './evem.mjs'
    import isnint from './isnint.mjs'
    import cint from './cint.mjs'

    /**
     * 佇列處理器，單生產者單消費者模式，核心使用迭代器，具有消息堵塞與可限定同時處理上限數量功能
     *
     * Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/queue.test.mjs Github}
     * @memberOf wsemi
     * @param {Integer} [takeLimit=0] 輸入同時處理數量整數，預設0，代表無限制
     * @returns {Object} 回傳事件物件，可呼叫事件on、push、get、cb、clear。on為監聽事件，需自行監聽message事件，push為加入最新佇列消息，get為回傳當前最早佇列消息，cb為於message事件內回調使迭代器可取得下一個佇列消息，clear為清空佇列
     * @example
     *
     * async function topAsync() {
     *
     *     async function test(takeLimit, timeCallBack) {
     *         return new Promise((resolve, reject) => {
     *
     *             //queue
     *             let q = queue(takeLimit)
     *             let n = 0
     *             let ms = []
     *
     *             //message
     *             q.on('message', function(qs) {
     *                 console.log('message', JSON.stringify(qs))
     *
     *                 //ms
     *                 ms.push(JSON.parse(JSON.stringify(qs)))
     *
     *                 //get
     *                 let v = q.get()
     *                 if (!v) {
     *                     return
     *                 }
     *                 console.log('get', v)
     *
     *                 setTimeout(function() {
     *                     console.log('cb', v)
     *
     *                     //cb
     *                     q.cb()
     *
     *                     //resolve
     *                     if (v === '$10') {
     *                         resolve(ms)
     *                     }
     *
     *                 }, timeCallBack)
     *
     *             })
     *
     *             //queues push 1~5
     *             setTimeout(function() {
     *                 console.log('queues push 1~5')
     *                 let t = setInterval(function() {
     *                     n += 1
     *                     q.push('$' + n)
     *                     if (n === 5) {
     *                         clearInterval(t)
     *                     }
     *                 }, 50)
     *             }, 1)
     *
     *             //queues push 6~10 by delay 1s
     *             setTimeout(function() {
     *                 console.log('queues push 6~10')
     *                 let t = setInterval(function() {
     *                     n += 1
     *                     q.push('$' + n)
     *                     if (n === 10) {
     *                         clearInterval(t)
     *                     }
     *                 }, 50)
     *             }, 500)
     *
     *         })
     *     }
     *
     *     console.log('test1')
     *     let r1 = await test(2, 1000)
     *     console.log(JSON.stringify(r1))
     *     // queues push 1~5
     *     // message ["$1"]
     *     // get $1
     *     // message ["$2"]
     *     // get $2
     *     // queues push 6~10
     *     // cb $1
     *     // message ["$3","$4","$5","$6","$7","$8","$9","$10"]
     *     // get $3
     *     // cb $2
     *     // message ["$4","$5","$6","$7","$8","$9","$10"]
     *     // get $4
     *     // cb $3
     *     // message ["$5","$6","$7","$8","$9","$10"]
     *     // get $5
     *     // cb $4
     *     // message ["$6","$7","$8","$9","$10"]
     *     // get $6
     *     // cb $5
     *     // message ["$7","$8","$9","$10"]
     *     // get $7
     *     // cb $6
     *     // message ["$8","$9","$10"]
     *     // get $8
     *     // cb $7
     *     // message ["$9","$10"]
     *     // get $9
     *     // cb $8
     *     // message ["$10"]
     *     // get $10
     *     // cb $9
     *     // cb $10
     *     // [["$1"],["$2"],["$3","$4","$5","$6","$7","$8","$9","$10"],["$4","$5","$6","$7","$8","$9","$10"],["$5","$6","$7","$8","$9","$10"],["$6","$7","$8","$9","$10"],["$7","$8","$9","$10"],["$8","$9","$10"],["$9","$10"],["$10"]]
     *
     *     console.log('test2')
     *     let r2 = await test(0, 500) //takeLimit=0, timeCallBack=500ms
     *     console.log(JSON.stringify(r2))
     *     // test2
     *     // queues push 1~5
     *     // message ["$1"]
     *     // get $1
     *     // message ["$2"]
     *     // get $2
     *     // message ["$3"]
     *     // get $3
     *     // message ["$4"]
     *     // get $4
     *     // message ["$5"]
     *     // get $5
     *     // queues push 6~10
     *     // message ["$6"]
     *     // get $6
     *     // cb $1
     *     // message ["$7"]
     *     // get $7
     *     // cb $2
     *     // message ["$8"]
     *     // get $8
     *     // cb $3
     *     // message ["$9"]
     *     // get $9
     *     // cb $4
     *     // message ["$10"]
     *     // get $10
     *     // cb $5
     *     // cb $6
     *     // cb $7
     *     // cb $8
     *     // cb $9
     *     // cb $10
     *     // [["$1"],["$2"],["$3"],["$4"],["$5"],["$6"],["$7"],["$8"],["$9"],["$10"]]
     *
     * }
     * topAsync().catch(() => {})
     *
     */
    function queue(takeLimit = 0) {
        let takeNow = 0 //目前執行數量
        let qs = []

        //check
        if (isnint(takeLimit)) {
            takeLimit = 0
        }
        takeLimit = cint(takeLimit)

        //ev
        let ev = evem()

        //get, like iterator
        function get() {
            if (qs.length > 0) {

                //add
                takeNow += 1

                //take first
                let r = qs.splice(0, 1)[0]

                return r
            }
            else {

                //null
                let r = null

                return r
            }
        }

        //cb
        function cb() {

            //minu
            takeNow -= 1
            if (takeNow < 0) {
                takeNow = 0
            }

            //emit
            if (qs.length > 0) {
                ev.emit('message', qs)
            }

        }

        //push
        function push(v) {

            //push
            qs.push(v)
            //console.log('push', v, qs)

            //emit
            if (takeLimit <= 0 || takeNow < takeLimit) {
                ev.emit('message', qs)
            }

        }

        //clear
        function clear() {
            takeNow = 0
            qs = []
        }

        //save
        ev.get = get
        ev.cb = cb
        ev.push = push
        ev.clear = clear

        return ev
    }

    export default queue