1、promise .then 的方法是想要改变原来的运行顺序,比如有异步的情况会异步执行,所以会按照指定的顺序运行下去;
2、promise .then的方法还用于有诸多返回的异步回调,需要调用异步的返回值的情况,这是可以很好的解决问题;

一.promise then 用法详解

Promise 是抽象的异步处理对象
构造方法:

let promies = new Promise((resolve, reject) => {
 resolve(); //异步处理 
});

Promise 有三个状态:

  • Fulfilled: has-resolved, 表示成功解决,这时会调用 onFulfilled.
  • Rejected: has-rejected, 表示解决失败,此时会调用 onRejected.
  • Pending: unresolve, 表示待解决,既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态.

  上面我们提到 Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolvereject.
  resolve函数的作用是,将 Promise 对象的状态从 未处理 变成 处理成功 (unresolved => resolved), 在异步操作成功时调用,并将异步操作的结果作为参数传递出去。
  reject函数的作用是,将 Promise 对象的状态从 未处理 变成 处理失败 (unresolved => rejected), 在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
  Promise 实例生成以后,可以用 then 方法和 catch 方法分别指定 resolved 状态和 rejected 状态的回调函数。

举例说明:代码示例:

(1)下面通过样例作为演示,我们定义做饭、吃饭、洗碗(cook、eat、wash)这三个方法,它们是层层依赖的关系,下一步的的操作需要使用上一部操作的结果。(这里使用 setTimeout 模拟异步操作)

//做饭
function cook(){
    console.log('开始做饭。');
    var p = new Promise(function(resolve, reject){        //做一些异步操作
        setTimeout(function(){
            console.log('做饭完毕!');
            resolve('鸡蛋炒饭');
        }, 1000);
    });
    return p;
}
 
//吃饭
function eat(data){
    console.log('开始吃饭:' + data);
    var p = new Promise(function(resolve, reject){        //做一些异步操作
        setTimeout(function(){
            console.log('吃饭完毕!');
            resolve('一块碗和一双筷子');
        }, 2000);
    });
    return p;
}
 
function wash(data){
    console.log('开始洗碗:' + data);
    var p = new Promise(function(resolve, reject){        //做一些异步操作
        setTimeout(function(){
            console.log('洗碗完毕!');
            resolve('干净的碗筷');
        }, 2000);
    });
    return p;
}

(2)使用 then 链式调用这三个方法:

cook()
.then(function(data){
    return eat(data);   //这里的data是cook()的返回值;
})
.then(function(data){
    return wash(data);     //这里的data是eat()的返回值;
})
.then(function(data){
    console.log(data);   //这里的data是wash()的返回值;
});

当然上面代码还可以简化成如下:

cook()
.then(eat)
.then(wash)
.then(function(data){
    console.log(data);
});

(3)运行结果如下:

开始做饭
做饭完毕
开始吃饭:鸡蛋炒饭
吃饭完毕
开始洗碗:一块碗一双筷子
洗碗完毕
干净的碗筷

  解释:上述构造方法中的两个参数resolve, reject即是改变promise的状态,resolve 方法把 Promise 的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调​,resolve, reject可抛出结果,作为then方法中函数的参数。then可接受两个参数,第一个处理Resolved状态的函数,第二个处理Rejected函数。如果只想处理成功,或者失败,对应参数可设置为null,只有一个参数则表示状态改变就执行,不区分状态结果。

另外(详细的用法,通过网络整理)
  catch()方法,它可以和 then 的第二个参数一样,用来指定 reject 的回调,另一个作用是,当执行 resolve 的回调(也就是上面 then 中的第一个参数)时,如果抛出异常了(代码出错了),那么也不会报错卡死 js,而是会进到这个 catch 方法中。
  all()方法,Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调, 会把所有异步操作的结果放进一个数组中传给 then。
  race()方法,race 按字面解释,就是赛跑的意思。race 的用法与 all 一样,只不过 all 是等所有异步操作都执行完毕后才执行 then 回调。而 race 的话只要有一个异步操作执行完毕,就立刻执行 then 回调。

  注意:其它没有执行完毕的异步操作仍然会继续执行,而不是停止。

二.Promise中then()方法使用,多次调用、链式调用

1.多次调用then

  多次调用then,每次res值都是new Promise的值;同一个Promise被多次调用;

const promise = new Promise((resolve, reject) => {
  resolve("hahaha")
})
// 1.同一个Promise可以被多次调用then方法
// 当我们的resolve方法被回调时, 所有的then方法传入的回调函数都会被调用
promise.then(res => {
  console.log("res1:", res)    //hahaha
})
promise.then(res => {
  console.log("res2:", res)    //hahaha
})
promise.then(res => {
  console.log("res3:", res)    //hahaha
})
const promise = new Promise((resolve, reject) => {
    resolve("hahaha")
})
// 1.同一个Promise可以被多次调用then方法
// 当我们的resolve方法被回调时, 所有的then方法传入的回调函数都会被调用
promise.then(res => {
    console.log("res1:", res)    //res1: hahaha
    return 9
}).then(res => {
    console.log("res2:", res)    //res2: 9
    return 8
}).then(res => {
    console.log("res3:", res)    //res3: 8
})

2.链式调用then

  then方法传入的 "回调函数: 可以有返回值
  then方法本身也是有返回值的, 它的返回值是Promise
  如果返回的是一个普通值(数值/字符串/普通对象/undefined), 那么这个普通的值被作为一个新的Promise的resolve值
  then的链式调用接受之前then返回的新Promise

const promise = new Promise((resolve, reject) => {
  resolve("hahaha")
})
promise.then(res => {
  return "aaaaaa"    
  //或包装Promise的resolve值返回
  // new Promise((resolve, reject) => {
  //   resolve("aaaaaa")
  // })
}).then(res => {               //这里.then的res是前一个then的返回值 resolve("aaaaaa")
  console.log("res:", res)     //aaaaaa
})

promise.then(res => {
  // 如果没有返回值如下
  //或包装Promise的resolve值返回
  // new Promise((resolve, reject) => {
  //   resolve("undefined")
  // })
}).then(res => {                   //这里.then的res是前一个then的返回值 resolve("undefined")
  console.log("res:", res)         //undefined
})

如果返回的是一个Promise

const promise = new Promise((resolve, reject) => {
  resolve("hahaha")
})
promise.then(res => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(111111)
    }, 3000)
  })
}).then(res => {
  console.log("res:", res)   //111111  3秒后打印 由返回的Promise状态决定
})

如果返回的是一个对象, 并且该对象实现了thenable

const promise = new Promise((resolve, reject) => {
  resolve("hahaha")
})
promise.then(res => {
  return {
    then: function(resolve, reject) {
      resolve(222222)
    }
  }
}).then(res => {
  console.log("res:", res)    //222222
})

三.Promise中的then、catch、finally总结

1、Promise的状态一经改变就不能再改变

const promise = new Promise((resolve, reject) => {
    resolve("success1");
    reject("error");
    resolve("success2");
});
promise
    .then(res => {
        console.log("then: ", res);
    }).catch(err => {
        console.log("catch: ", err); //没错误,不执行;
    })
  // then:success1

2、thencatch都会返回一个新的Promise。
3、catch不管被连接到哪里,都能捕获上层未捕捉过的错误

const promise = new Promise((resolve, reject) => {
  reject("error");
  resolve("success2");
});
promise
.then(res => {
    console.log("then1: ", res);
  }).then(res => {
    console.log("then2: ", res);
  }).catch(err => {
    console.log("catch: ", err);
  }).then(res => {
    console.log("then3: ", res);
  })
//"catch: " "error"  验证第三点的总结
//"then3: " undefined  验证第二点总结

4、在Promise中,返回任意一个非 promise 的值都会被包裹成 promise 对象,例如return 2会被包装为return Promise.resolve(2)。
5、Promise 的 .then 或者 .catch 可以被调用多次, 但如果Promise内部的状态一经改变,并且有了一个值,那么后续每次调用.then或者.catch的时候都会直接拿到该值。

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('timer')
        resolve('success')
    }, 2000)
})
const start = Date.now();
promise.then(res => {
    console.log(res, Date.now() - start)     //(Date.now() - start)的结果是一个时间段,单位是毫秒;
})
promise.then(res => {
    console.log(res, Date.now() - start)
})

//'timer'
//'success' 2023
//'success' 2023
// Promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次。
// 或者说 promise 内部状态一经改变,并且有了一个值,那么后续每次调用 .then 或者 .catch 都会直接拿到该值。

6、.then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获

Promise.resolve().then(() => {
  return new Error('error!!!')
}).then(res => {
  console.log("then: ", res)
}).catch(err => {
  console.log("catch: ", err)
})
 
//"then: " "Error: error!!!"
 
// 这验证了第4点和第6点,返回任意一个非 promise 的值都会被包裹成 promise 对象,
// 因此这里的return new Error('error!!!')也被包裹成了return Promise.resolve(new Error('error!!!'))。

当然如果你抛出一个错误的话,可以用下面的任意一种:

return Promise.reject(new Error('error!!!'));
// or
throw new Error('error!!!')

7、.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。
8、.then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传。

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)
 
// 1
// 第一个then和第二个then中传入的都不是函数,一个是数字类型,一个是对象类型
// 因此发生了透传,将resolve(1) 的值直接传到最后一个then里。

9、.then方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为catch是.then第二个参数的简便写法。
10、.finally方法也是返回一个Promise,他在Promise结束的时候,无论结果为resolved还是rejected,都会执行里面的回调函数。

  • .finally()方法不管Promise对象最后的状态如何都会执行
  • .finally()方法的回调函数不接受任何的参数,也就是说你在.finally()函数中是没法知道Promise最终的状态是resolved还是rejected的
  • ③它最终返回的默认会是一个上一次的Promise对象值,不过如果抛出的是一个异常则返回异常的Promise对象。

四、Promise可以保证异步执行的顺序

通过Promise可以保证异步执行的顺序,如下所示:

var p1 = new Promise(function(resolve,reject){
    fs.readFile("./file1.txt",function(err,data){
        if(err){        //假如没有新建被读的文件;则执行返回错误;
            reject(err);    //返回错误;
        }else{
            resolve(data.toString());
        }
    });
});
var p2 = new Promise(function(resolve,reject){
    fs.readFile("./file2.txt",function(err,data){
        if(err){         //假如没有新建被读的文件;则执行返回错误;
            reject(err);       //返回错误;
        }else{
            resolve(data.toString());
        }
    });
});
//通过数组中的顺序,控制异步输出的顺序
Promise.all([p1,"哈哈哈",p2]).then(function(datas){
    console.log(datas);
},function(errs){
    console.log(errs);
});

优化后的结果,如下所示:
在这里插入图片描述
  通过以上示例发现,Promise可以通过消息的传递,保证异步操作的顺序。

五.两个或多个嵌套的promise .then 模块

const promise = new Promise((resolve, reject) => {
    resolve("hahaha")
    const promise = new Promise((resolve, reject) => {
        console.log(999)
        resolve(989)
    }).then(res => {
        console.log(res)
        return 0
    })

})
promise.then(res => {
    console.log("res1:", res)    //res1: hahaha
    return 9
}).then(res => {
    console.log("res2:", res)    //res2: 9
    return 8
}).then(res => {
    console.log("res3:", res)    //res3: 8
})

执行结果

999
989
res1: hahaha
res2: 9
res3: 8

六、Promise .then的用意就是使异步代码的执行过程可以按照指定的顺序从上到下依次执行,打破了异步代码的局限性;

示例:
使用结构:

const promise = new Promise((resolve, reject) => {
    resolve("hahaha")
})
promise.then(res => {
    return new Promise((resolve, reject) => {
        console.log(999)
        setTimeout(() => {
            console.log(888)
            resolve(111111)
        }, 3000)
    })
}).then(res => {
    console.log("哈哈哈")
    console.log("res:", res)   //111111  3秒后打印 由返回的Promise状态决定
})

用上promise .then结构实际的执行结果:
及即使有延迟也要把延迟函数内部的函数执行一遍,即使是一个循环函数,也要执行一次之后再执行循环之外的内容

999
888
哈哈哈
res: 111111

本来不用promise .then结构执行:

var i = 0
console.log(999)
refreshId = setInterval(() => {

    console.log(888)
    i++
    if (i >= 3) {
        clearInterval(refreshId);
    }
}, 1000)
console.log("哈哈哈")
console.log("res:")  

执行结果:

999
哈哈哈
res:
888
888
888

七、promise .then程序中有循环的情况:

1、promise .then位于循环内部;

var i = 0, y = 9;
refreshId = setInterval(() => {
    const promise = new Promise((resolve, reject) => {
        console.log(789);
        resolve("hahaha")
    })
    promise.then(res => {
        return new Promise((resolve, reject) => {
            console.log(999)


            console.log(888)
            // if (y = 9) {
            //     clearInterval(refreshId);
            // }
            resolve(111111)
            i++
            if (i >= 6) {
                clearInterval(refreshId);
            }
        })
    }).then(res => {
        console.log("哈哈哈")
        console.log("res:", res)   //111111  3秒后打印 由返回的Promise状态决定
    })
}, 1000)

执行结果:以下重复打印6次

789
999
888
哈哈哈
res: 111111

如果没有注释掉

// if (y = 9) {
            //     clearInterval(refreshId);
            // }
            ```

则会打印一次,可见虽有退出循环,可是.then中的内容依旧要执行,整个完整的程序也至少要执行完整的一遍;

2、promise .then内部有循环的情况

var i = 0, y = 9;
const promise = new Promise((resolve, reject) => {
    console.log(789);
    resolve("hahaha")
})
promise.then(res => {
    return new Promise((resolve, reject) => {
        console.log(999)
        refreshId = setInterval(() => {

            console.log(888)
            resolve(111111)
            i++
            if (i >= 6) {
                clearInterval(refreshId);
            }
        }, 1000)
    })
}).then(res => {
    console.log("哈哈哈")
    console.log("res:", res)   //111111  3秒后打印 由返回的Promise状态决定
})

执行结果:【打破了异步的顺序打印了888,都执行一遍之后,只执行循环】

789
999
888
哈哈哈
res: 111111
888
888
888
888
888

八、浅谈Promise怎么取消或中断

  如果promise .then模块位于一个循环中,假如在promise .then中有一个判断,当条件语句为假时,要终止执行promise .then模块中条件语句之后的代码;也就是说我要在条件语句执行为false时,在这个位置退出循环,退出程序,要怎么做呢;

1、使用returnclearInterval(refreshId)(跳出循环)可不可以呢

(1)在单次循环里:

var i = 0, y = 9;
refreshId = setInterval(() => {
    const promise = new Promise((resolve, reject) => {
        console.log(789);
        resolve("hahaha")
    })
    promise.then(res => {
        return new Promise((resolve, reject) => {
            console.log(999)
            console.log(888)
            if (y = 9) {
                clearInterval(refreshId);
                return
            }
            resolve(111111)
            i++
            if (i >= 3) {
                clearInterval(refreshId);
                // return

            }
        })
    }).then(res => {
        console.log("哈哈哈")
        console.log("res:", res)   //111111  3秒后打印 由返回的Promise状态决定
    })
}, 1000)

执行结果:

789
999
888

没有执行下面的打印:

console.log("哈哈哈")
console.log("res:", res) 

如果没有return,则会完整执行一遍,再退出循环;
(2)在多次循环里

var i = 0, y = 9;
refreshId = setInterval(() => {
    const promise = new Promise((resolve, reject) => {
        console.log(789);
        resolve("hahaha")
    })
    promise.then(res => {
        return new Promise((resolve, reject) => {
            console.log(999)
            console.log(888)
            resolve(111111)
            i++
            if (i >= 3) {
                clearInterval(refreshId);
                return
            }
        })
    }).then(res => {
        console.log("哈哈哈")
        console.log("res:", res)   //111111  3秒后打印 由返回的Promise状态决定
    })
}, 1000)

执行结果:有无下面的return,程序都是一样执行,都是完整的执行打印;
  上面这段程序如果去掉 clearInterval(refreshId);,只留return结果就是一致执行全部都打印,都执行,不会停下来;

总结:如果有循环,直接退出循环的情况,有return会直接只执行return之上的执行、打印,之下的不执行;如果有循环把退出循环的语句替换为return,则会循环执行打印,但每次return之下的内容不执行打印;如果有多次循环,在条件语句中判断合适退出循环,这时,退出程序下有return,但是return的作用没有显现,可有可无;

2、浅谈Promise怎么取消或中断

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐