9102,Promise 和 Async 你会真的会用了吗?

没什么,只是但愿你取的人是我!     --- 时间旅行者的妻子ajax

前言

遥想当年刚接触 JavaScript 时被回调地狱支配的恐惧,😂😂😂!json

解救之道,就在其中啊 --- Promise 和 Async/awaitapi

推荐 vscode 插件 Code Runner,能够运行代码片断。学习利器啊。不过是基于 Node 运行环境数组

Promise

一个 Promise有如下几种状态: 状态一旦改变,便不能更改。promise

pending: 初始状态,既不是成功,也不是失败状态。等待状态转变异步

fulfilled: 意味着操做成功完成。then 处理逻辑async

rejected: 意味着操做失败。能够在 catch 处理逻辑ide

每个 then 都会返回一个新的 Promise 实例函数

Api

// 建立一个 fulfilled 状态的 Promise 实例。
Promise.resolve();
// 建立一个 rejected 状态的 Promise 实例。
Promise.reject();
// then 的第一个参数 resolve {Function} 处理 fulfilled 状态,
// 第二参数rejected 处理 rejected 状态。
// 若是 then 处理了 rejected 状态,那么返回的 Promise 实例为 
// fulfilled,不然会返回的实例为 rejected。
Promise.prototype.then(resolve,rejected);
// 拦截 rejected 状态执行,并返回一个 fulfilled 状态的 Promise 实例
Promise.prototype.catch();
// 无论什么状态最后都会执行
Promise.prototype.finally();
// 执行多个 promise 都执行玩了并将结果返回 Array [promiseRet,promiseRet]
Promise.all([promise,promise]);
// 返回昀行最快的那个 promise 的结果 
Promise.race([promise,promise]);
复制代码

开胃小菜

  • demo1
const demo1 = function _demo1(num) {
    return new Promise((resolve, reject) => {
        if (num % 2 === 0) {
            // pending 状态 到 fulfilled
            resolve(num);
            return;
        }
        // pending 状态 到 rejected 
        reject(num);
    });
};
demo1(2)
    .then(
        // 处理 fulfilled 状态
        resolveData => {
            console.log('resolve-', resolveData);
        },
        // 处理 rejected 状态,以后的 catch 不会被触发。
        rejectData => {
            console.log('reject', rejectData);
        }
    )
    .then(data => console.log('依然能够调用哦,只是 data undefined', data));
    
    // 2 是偶数,状态转变 fulfilled 执行对应回调。
    // resolve- 2
    // 依然能够调用哦,只是 data undefined undefined
复制代码
  • demo2
const demo1 = function _demo1(num) {
    return new Promise((resolve, reject) => {
        if (num % 2 === 0) {
            resolve(num);
            return;
        }
        reject(num);
    });
};
demo1(1)
    .then(
        resolveData => {
            console.log('resolve-', resolveData);
        },
        rejectData => {
            console.log('reject', rejectData);
        }
    )
    .then(data => console.log('依然能够调用哦,只是 data undefined-', data))
    .catch(error => console.log('catch:', error));
// 运行结果 1 为偶数,then 处理了对应 rejected,状态不会传递下去,不会执行 catch
// reject 1
//依然能够调用哦,只是 data undefined- undefined
复制代码
  • demo3
const demo1 = function _demo1(num) {
    return new Promise((resolve, reject) => {
        if (num % 2 === 0) {
            resolve(num);
            return;
        }
        reject(num);
    });
};
demo1(3)
    .then(resolveData => {
        console.log('resolve-', resolveData);
    })
    .then(data => console.log('依然能够调用哦,只是 data undefined-', data))
    .catch(error => {
        console.log('catch-', error);
        return '我处理 rejected 了';
    })
    // 验证 catch 拦截了 rejected ,并返回了 fulfilled 实例
    .then(data => console.log('catch 处理以后的 then-', data));
    // 3 为奇数,状态转变为 rejected ,而后 then 都没有处理其状态, rejected 一直被传递到 catch 处理。
    // catch- 3
    // catch 处理以后的 then- 我处理 rejected 了
复制代码

推荐 then 只处理 fulfilled 状态,catch 拦截 rejected 状态处理逻辑学习

promise.then(resolve=>{}).then(resolve=>{}).then(resolve=>{}).catch(error=>{});
复制代码

Promise 封装 JQuery-Ajax

class AjaxUtil {
    static get(url, otherSetting = {}) {
        return new Promise((resolve, reject) => {
            const ajaxGetSetting = {
                url: url,
                type: 'GET',
                dataType: 'json',
                success: data => {
                    resolve(data);
                },
                error: error => {
                    reject(error);
                }
            };
            $.ajax(Object.assign(otherSetting, ajaxGetSetting));
        });
    }
}
// 不用再传 callback ,爽歪歪了吧。
AjaxUtil.get('/api/videos').then(data => {
    document.getElementById('div').innerHTML = JSON.stringify(data);
});
复制代码

Promise.all()

/** * const promise=Promise.all([promise,promise...]); * @description: Promise.all 返回 Promise 实例,数组里面有一个状态是 reject * 返回的实例的状态为 reject */
复制代码
  • Demo
const sleep = function _sleep(ms) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, ms);
    });
};
console.log('start');
const sleep1 = sleep(1000).then(() => {
    console.log('休息一秒');
    return '跟他説抱歉,我要去找个女孩';
});
const sleep2 = sleep(2000).then(() => {
    console.log('休息两秒');
    return '忘记应该忘记的,面对将来一切能够出现的。';
});
const sleep3 = sleep(3000).then(() => {
    console.log('休息三秒');
    return '孩子,生活不是靠书本和想固然来的,是靠心去体会的。';
});
const sleep4 = sleep(4000).then(() => {
    console.log('休息四秒');
    return '总有人有很差的时候,但这也会让你回忆起从前未曾在乎的美好。';
});
const ret2 = Promise.all([sleep1, sleep2, sleep3, sleep4]);
ret2.then(data => {
    // data 为 全部 promise resolve 的 结果组成的数组
    console.log(data);
});
console.log('end');
复制代码
// 执行结果为
start
end
休息一秒
休息两秒
休息三秒
休息四秒
[
  '跟他説抱歉,我要去找个女孩',
  '忘记应该忘记的,面对将来一切能够出现的。',
  '孩子,生活不是靠书本和想固然来的,是靠心去体会的。',
  '总有人有很差的时候,但这也会让你回忆起从前未曾在乎的美好。'
]
复制代码

Promise.all 能够在想拿到多个异步事件执行结果的而后进行后续逻辑处理很方便。可是 Promise.all 不会阻塞后续代码的执行。结合 async/await 用同步编码的思惟编写代码很舒服。

Demo2

验证 Promise.all 参数中的 promise 有一个状态是 rejected 返回的 promise 是 rejected

const sleep = function _sleep(ms) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, ms);
    });
};
const sleep1 = sleep(1000).then(() => {
    console.log('休息一秒');
    return '跟他説抱歉,我要去找个女孩';
});
const sleep2 = sleep(2000).then(() => {
    console.log('休息两秒');
    return '忘记应该忘记的,面对将来一切能够出现的。';
});
const sleep3 = sleep(3000).then(() => {
    console.log('休息三秒');
    return '孩子,生活不是靠书本和想固然来的,是靠心去体会的。';
});
const sleep4 = sleep(4000).then(() => {
    console.log('休息四秒');
    return '总有人有很差的时候,但这也会让你回忆起从前未曾在乎的美好。';
});
const sleep5 = sleep(5000).then(() => {
    console.log('休息五秒');
    throw new Error('模拟测试失败状况');
});
const ret2 = Promise.all([sleep1, sleep2, sleep3, sleep4, sleep5]);
ret2.then(data => {
    console.log(data);
    // 执行 catch
}).catch(error => {
    console.log(error.message);
});
复制代码
休息一秒
休息两秒
休息三秒
休息四秒
休息五秒
模拟测试失败状况
复制代码

Promise.race([promise,promise])

最早改变状态,就返回那个 promise

const sleep = function _sleep(ms) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, ms);
    });
};
const sleep2 = sleep(2000).then(() => {
    console.log('休息两秒');
    return '你能够了解世间万物,但追根溯源的惟一途径即是亲身尝试。';
});
const sleep3 = sleep(3000).then(() => {
    console.log('休息三秒');
    return '人终究是一个孤独的个体,纵使你已经拥有了他人的怀抱,这其中,也许人与人之间惟一不一样的,只是你把孤独藏在哪里。';
});
const sleep4 = sleep(4000).then(() => {
    console.log('休息四秒');
    return '躲避和不信任,是由于曾经被应该爱个人人遗弃。';
});
const ret = Promise.race([sleep2, sleep3, sleep4]);
ret.then(data => {
    console.log(data);
});
复制代码
休息两秒
你能够了解世间万物,但追根溯源的惟一途径即是亲身尝试。
休息三秒
休息四秒
复制代码

Async/await

async/await ,一直用一直爽。

async 函数返回一个 Promise实例。遇到 await 会代码添加到微任务队列,而后从函数中返回继续执行后续代码。

async/await 用于 Function 上。而且 await 不能单独出现,须要和 async 一块儿。

const async = async function _async() {
    // 执行遇到 await 将任务添加到微任务队列,而后返回。继续执行后续代码
    const awaitRet = await Promise.resolve(1).then(data => {
        console.log('resolve-then');
        return data;
    });
    console.log(awaitRet);
    console.log('await 后面代码');
};
console.log('start');
async();
console.log('end');
复制代码
start
end
resolve-then
1
await 后面代码
复制代码
const awaitRet = await Promise.resolve(1).then(data => {
        console.log('resolve-then');
        return data;
    });
    console.log(awaitRet);
    console.log('await 后面代码');
    // 上面代码能够简单理解为

Promise.resolve()
    .then(() => {
        const awaitRet = await Promise.resolve(1).then(data => {
        console.log('resolve-then');
        return data;
        });
    })
    // await 后面代码添加到微任务队列
    .then(() => {
        console.log(awaitRet);
        console.log('await 后面代码');
    });
复制代码

Demo1

const awaitFunc = function _awaitFunc() {
    return Promise.resolve('awaitFunc').then(data => {
        console.log(data);
        return 'awaitFunc-then-return-data';
    });
};
const async = async function _async() {
    // 等待 await 执行完以后,再将后续的代码放入微任务队列
    await awaitFunc().then(data => {
        console.log(data);
    });
    console.log('awaitFunc 执行完在打印');
};
async();
复制代码
awaitFunc
awaitFunc-then-return-data
awaitFunc 执行完在打印
复制代码

Demo2

await 阻塞的后续代码放入微任务队列?真的是这样吗?来个 demo 验证个人想法。

const awaitFunc = function _awaitFunc() {
    return Promise.resolve('awaitFunc').then(data => {
        console.log(data);
        return 'awaitFunc-then-return-data';
    });
};
const async = async function _async() {
    // 等待 await 执行完以后,再将后续的代码放入微任务队列
    setTimeout(() => {
        console.log('验证加入了微任务队列---1');
    }, 0);
    await awaitFunc().then(data => {
        console.log(data);
        setTimeout(() => {
            console.log('验证加入了微任务队列---2');
        }, 0);
    });
    console.log('awaitFunc 执行完在打印');
};
async();

复制代码
awaitFunc
awaitFunc-then-return-data
awaitFunc 执行完在打印
验证加入了微任务队列---1
验证加入了微任务队列---2
复制代码

console.log('awaitFunc 执行完在打印') 被放入了宏任务队列,那么这段代码不会比 setTimeout 先执行。所以 await 阻塞的后续代码,放入到了当前事件循环的微任务队列。全部的微任务也会在本次事件循环被执行完毕。

Promise&async/await 使用场景

用同步的思惟编写异步的代码,确实比较符合咱们的思惟习惯。

const sleep = function _sleep(ms, data) {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(`休息了 ${ms} 秒`);
            resolve(data);
        }, ms);
    });
};
console.log('start');

const data = [
    '跟他説抱歉,我要去找个女孩',
    '忘记应该忘记的,面对将来一切能够出现的。',
    '孩子,生活不是靠书本和想固然来的,是靠心去体会的。',
    '总有人有很差的时候,但这也会让你回忆起从前未曾在乎的美好。'
];
const asyncFunc = function _async(data) {
    console.log('asyncFunc-start');
    const promises = [];
    for (let index = 0; index < data.length; index++) {
        promises.push(sleep(index, data[index]));
    }
    const ret = Promise.all(promises);
    console.log('处理完在开始');
    console.log('asyncFunc-end');
    return ret;
};
const retPromise = asyncFunc(data);
retPromise.then(data => {
    console.log(data);
});
console.log('end');

复制代码
start
asyncFunc-start
处理完在开始
asyncFunc-end
end
休息了 0 秒
休息了 1 秒
休息了 2 秒
休息了 3 秒
[
  '跟他説抱歉,我要去找个女孩',
  '忘记应该忘记的,面对将来一切能够出现的。',
  '孩子,生活不是靠书本和想固然来的,是靠心去体会的。',
  '总有人有很差的时候,但这也会让你回忆起从前未曾在乎的美好。'
]
复制代码

上述代码呢我想让 asyncFunc 里面顺序执行,先处理 Promise.all ,再继续 await 后面的代码。

  • 使用 async/await 改写。
const sleep = function _sleep(ms, data) {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(`休息了 ${ms} 秒`);
            resolve(data);
        }, ms);
    });
};
console.log('start');

const data = [
    '跟他説抱歉,我要去找个女孩',
    '忘记应该忘记的,面对将来一切能够出现的。',
    '孩子,生活不是靠书本和想固然来的,是靠心去体会的。',
    '总有人有很差的时候,但这也会让你回忆起从前未曾在乎的美好。'
];
const asyncFunc = async function _async(data) {
    console.log('asyncFunc-start');
    const promises = [];
    for (let index = 0; index < data.length; index++) {
        promises.push(sleep(index, data[index]));
    }
    const ret = await Promise.all(promises);
    console.log('处理完在开始');
    console.log('asyncFunc-end');
    return ret;
};
const retPromise = asyncFunc(data);
retPromise.then(data => {
    console.log(data);
});
console.log('end');

复制代码
start
asyncFunc-start
end
休息了 0 秒
休息了 1 秒
休息了 2 秒
休息了 3 秒
处理完在开始
asyncFunc-end
[
  '跟他説抱歉,我要去找个女孩',
  '忘记应该忘记的,面对将来一切能够出现的。',
  '孩子,生活不是靠书本和想固然来的,是靠心去体会的。',
  '总有人有很差的时候,但这也会让你回忆起从前未曾在乎的美好。'
]
复制代码

能够看到 await 后面的代码须要等到 Promise.all 微任务所有执行完毕才能够开始。注意那个 end 打印时机,其实 async/await 也是基于事件循环,并非阻塞代码执行。否则那个 end 应该最后打印才是。

在循环中使用 async/await

forof,forin,fori 循环能够感知 await

const data = [1, 2, 3];
const async = async function _async(data) {
    for (const index in data) {
        const item = data[index];
        const ret = await Promise.resolve(item)
            .then(data => {
                console.log('第一个 then');
                return data;
            })
            .then(data => {
                console.log('第二个 then');
                return data;
            });
        console.log('await 返回值', ret);
        console.log('await 以后的代码');
    }
};
async(data);

复制代码
// fori
const data = [1, 2, 3];
const async = async function _async(data) {
    for (let index = 0; index < data.length; index++) {
        const item = data[index];
        await Promise.resolve(item)
            .then(data => {
                console.log('第一个 then');
                return data;
            })
            .then(data => {
                console.log('第二个 then');
                return data;
            });
        console.log('await 以后的代码');
    }
};
async(data);

复制代码
// forof
const data = [1, 2, 3];
const async = async function _async(data) {
    for (const item of data) {
        await Promise.resolve(item)
            .then(data => {
                console.log('第一个 then');
                return data;
            })
            .then(data => {
                console.log('第二个 then');
                return data;
            });
        console.log('await 以后的代码');
    }
};
async(data);
复制代码
第一个 then
第二个 then
await 以后的代码
第一个 then
第二个 then
await 以后的代码
第一个 then
第二个 then
await 以后的代码
复制代码

forin,forof,fori 都能感知 await

forEach 不能感知 await

const data = [1, 2, 3];
const async = async function _async(data) {
    data.forEach(async item => {
        const ret = await Promise.resolve(item)
            .then(data => {
                console.log('第一个 then');
                return data;
            })
            .then(data => {
                console.log('第二个 then');
                return data;
            });
        console.log('await 返回值', ret);
        console.log('await 以后的代码');
    });
};
async(data);
复制代码
第一个 then
第一个 then
第一个 then
第二个 then
第二个 then
第二个 then
await 返回值 1
await 以后的代码
await 返回值 2
await 以后的代码
await 返回值 3
await 以后的代码
复制代码
相关文章
相关标签/搜索