promise串行封装

想要一个Promise.queue,类似Promise.all,接受一个装了很多个promise对象的数组,但逐个依次决议(不像all一样是并发的,这里想要的是串行一个接一个的完成决议),获得一个结果数组,不考虑参数传递,google了一些答案,感觉都不太满意,求教大家,谢谢

阅读 7.8k
5 个回答

明白你的意思了,你要这么实现的话不能传入 Promise,因为在你创建 Promise 的一刻它就开始 pending 等待 settlement 了。

你应该传入一系列的 executors ,等上一个 Promise resolved 之后再创建新的 Promise。

数组可以放入函数,会自动 new Promise 并执行函数,其它类型则直接 resolve 掉。如果有 reject 返回第一个。

function promiseQueue (executors) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(executors)) { executors = Array.from(executors) }
    if (executors.length <= 0) { return resolve([]) }

    var res = []
    executors = executors.map((x, i) => () => {
      var p = typeof x === 'function' ? new Promise(x) : Promise.resolve(x)
      p.then(response => {
        res[i] = response
        if (i === executors.length - 1) {
          resolve(res)
        } else {
          executors[i + 1]()
        }
      }, reject)
    })
    executors[0]()
  })
}

下面例子可以看到,前面异步的时间更长,但依然按顺序执行

function alarm (msg, time) {
  return resolve => {
    setTimeout(() => {
      console.log(msg)
      resolve(msg)
    }, time)
  }
}

promiseQueue([alarm('1', 4000), alarm('2', 1000), 12, 'hellp', alarm('3', 3000)]).then(x => console.log(x))

promiseQueue([() => Promise.reject('error1'), 12, 'hellp', () => Promise.reject('error2')]).then(null, e => console.log(e))

接受一个装了很多个promise对象的数组,但逐个一次执行,你仔细想一下,在JS里面什么是可执行的?Promise

事实上能够被执行的只有函数,所以我理解的是:你的入参应该是一个函数数组,每个函数都会返回一个Promise只有在这个Promiseresolve,或者reject之后,其后面的一个函数才会被执行。如果是这样的话就很简单了:

function executeWaterfall<T>(...executors: Array<() => T|Promise<T>>): Promise<T[]> {
    const output: T[] = []
    let p: Promise<T> = Promise.resolve<T>(void 0)
    for (const executor of executors) {
        p = p.then(executor).then((data) => output.push(data))
    }
    return p.then(() => output)
}

// 用例
executeWaterfall<number>(
    () => 0,
    () => new Promise((resolve) => setTimeout(() => resolve(1), 1e3)),
    () => Promise.resolve(2),
).then((output) => {
    console.log(output) // [0, 1, 2]
})

这里面没有考虑reject的情况:如果中间任何一个函数reject了,则返回会被reject

https://jsfiddle.net/z584kzue/2/

容我安利 promise的队列模块,支持设置并发,重试次数,超时时间
promise-queue-plus

var Queue = require('promise-queue-plus');
var q = Queue.Promise; //a Promise utils;

//Realize a queue with a maximum concurrency of 1
var queue1 = new Queue(1,{
        "retry":0               //Number of retries
        ,"retryIsJump":false     //retry now? 
        ,"timeout":0            //The timeout period
    });

//a return promise function
function testfn(i){
    return new Promise(function(resolve,reject){
        setTimeout(function(){
            resolve(i)
        },300)
    })
}
var log = function(msg){ console.log(msg); }

queue1.push(testfn,[1]) //add job (FIFO)
.then(log); 

queue1.push(function(){return 2;}) //The normal function returns a promise according to the Promise / A + rule
.then(log);

queue1.unshift(testfn,[0]) //add job (LIFO)
.then(log);

queue1.addLikeArray([3,4],testfn,{'workResolve':log}) //Add multiple jobs with Array, Work done will execute 'workResolve'
.then(log) 

queue1.addLikeProps({'a':5,'b':6,'c':7},testfn,{'workResolve':log}) //Add multiple jobs with Map,
.then(log)

//queue1.start(); //queue start;
queue1.go(testfn,['go']).then(log) 
/*
 Equivalent to:
    queue1.push(testfn,['go']).then(console.log);
    queue1.start(); 
* In general, it is convenient to use the 'go'
*/

// Output:
/* 
0
1
2
3
4
[ 3, 4 ]
5
6
7
{ a: 5, b: 6, c: 7 }
go
*/

如果你是想加多个promise函数拿数组可以用这个API

queue.addArray([
        [testfn,[0]],
        [testfn,[1]],
        [testfn,[2]],
        [testfn,[3]]
    ],true).then(function(arr){
        console.log(arr);
    })
// [0,1,2,3]

传入Promise是不行的
你需要传入执行器 then 函数来 schedule 任务

const array = [then,then,then]

async function queue(array){
    const result = []
    result.push(await new Promise(array.unshift()))
    return result
}

// 返回 Promise
const result = queue(array)

https://github.com/jun-lu/pro...

这个库可以帮你解决串行问题,而且可以串行和并行叠加

比如


new PromiseAsync( Promise.resolve(1), Promise.resolve(2))
  .flat((n1, n2)=>{
    return Promise.resolve(n1 + n2);
  })
  .subscribe((data)=>{
    // data=3...
  })
  .start()
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题